ping reply `result.timestamp` is JSON number on Windows but JSON string on Linux at the same CLI version (1.0.51) · Issue #3444 · github/copilot-cli · GitHub
Skip to content

ping reply result.timestamp is JSON number on Windows but JSON string on Linux at the same CLI version (1.0.51) #3444

Description

@lonegunmanb

ping JSON-RPC reply: timestamp wire type differs across platforms at the same CLI version (1.0.51)

Summary

At the same CLI version (1.0.51 / 1.0.51-2) the ping JSON-RPC
reply serializes the timestamp field with two incompatible JSON
types
depending on the host platform:

Host copilot --version Raw timestamp bytes JSON type
Windows GitHub Copilot CLI 1.0.51-2 1779352370134 NUMBER (epoch ms, fits int64)
Linux (container) GitHub Copilot CLI 1.0.51 "2026-05-21T08:29:54.042Z" STRING (ISO 8601)

Both samples are real captures via a hand-rolled JSON-RPC stdio harness
(no SDK, no wrapper) running the official flags
--headless --no-auto-update --log-level debug --stdio.

This breaks every typed downstream that expects a stable schema — most
notably copilot-sdk/go (filed sibling issue), where
PingResponse.Timestamp is int64 and the SDK refuses to start on
Linux at 1.0.51 with:

json: cannot unmarshal string into Go struct field PingResponse.timestamp of type int64

Beyond the SDK breakage, having the same RPC return two unrelated value
types depending on platform is a substantive schema violation in its own
right.

Reproduction

Tiny Go program at tmp/sdk-repro/cmd/captureping (no SDK
dependency — only encoding/json + os/exec):

$ go run ./cmd/captureping
=== HOST ===
go_os=...  go_arch=...  go_version=...
copilot_path=...
copilot_version=GitHub Copilot CLI 1.0.51...

spawning: copilot [--headless --no-auto-update --log-level debug --stdio]
writing request: {"id":1,"jsonrpc":"2.0","method":"ping","params":{"message":""}}
=== RAW RESPONSE BYTES ===
{"jsonrpc":"2.0","id":1,"result":{...}}
=== RESULT FIELD KINDS ===
  message          raw="pong" kind=STRING
  protocolVersion  raw=3 kind=NUMBER
  timestamp        raw=... kind=NUMBER | STRING

Run on both platforms with the same flags and compare the final
timestamp ... kind= line.

Actual captures

Windows host, copilot.bat 1.0.51-2:

copilot_version=GitHub Copilot CLI 1.0.51-2.

=== RAW RESPONSE BYTES ===
{"jsonrpc":"2.0","id":1,"result":{"message":"pong","timestamp":1779352370134,"protocolVersion":3}}

=== RESULT FIELD KINDS ===
  message          raw="pong"           kind=STRING
  protocolVersion  raw=3                kind=NUMBER
  timestamp        raw=1779352370134    kind=NUMBER

Linux container, copilot 1.0.51:

copilot_version=GitHub Copilot CLI 1.0.51

=== RAW RESPONSE BYTES ===
{"jsonrpc":"2.0","id":1,"result":{"message":"pong","timestamp":"2026-05-21T08:29:54.042Z","protocolVersion":3}}

=== RESULT FIELD KINDS ===
  message          raw="pong"                              kind=STRING
  protocolVersion  raw=3                                   kind=NUMBER
  timestamp        raw="2026-05-21T08:29:54.042Z"          kind=STRING

Why this is the CLI's problem, not the SDK's

  • The CLI is the authoritative definition of the wire protocol. Whatever
    it sends is the schema for all client SDKs (Node, .NET, Python, Go).
  • Asking every SDK to grow a per-field union type to absorb platform
    inconsistencies pushes downstream complexity outward forever.
  • The platform delta proves the inconsistency is internal to the CLI
    build pipeline (most likely a Date#toJSON() vs
    Date.now() / BigInt codepath that gets compiled / bundled
    differently per OS target).

Suggested fix

Pick one canonical wire representation for result.timestamp and
emit it consistently on every platform. Either:

  • A. Epoch milliseconds as a JSON number (matches what the Go SDK's
    PingResponse.Timestamp int64 and the field's existing
    semantics imply — and what the Windows build already does).
  • B. ISO 8601 string (more self-documenting, but requires breaking
    schema changes in all SDKs).

(A) is the cheaper fix and aligns with what existing typed SDKs expect.

Either way, please also add a wire-format regression test that asserts
the JSON typeof of every public RPC result field, so that future
serializer / bundler changes can't reintroduce a platform-specific
divergence silently.

Sibling issue

github/copilot-sdk — request a tolerant UnmarshalJSON on
PingResponse so the Go SDK can survive both wire shapes during any
rollout / mixed-fleet window. (URL to be filled in once both issues are
opened.)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:non-interactiveNon-interactive mode (-p), CI/CD, ACP protocol, and headless automation

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions