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

@lonegunmanb

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.)

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions