Skip to content

Session resume fails when masked code-change metrics make session.shutdown counters strings #5023

Description

@dewanymca

Describe the bug

A persisted Copilot CLI session can become permanently unresumable when code-change counters in a file-editing tool's toolTelemetry.metrics are stored as masked strings instead of numbers.

Observed behavior on a programmatic CLI session:

  1. A file-editing tool completes with nonnegative integer linesAdded / linesRemoved metrics.
  2. The post-tool hook receives those integer values and reports that it did not modify the result.
  3. The persisted tool.execution_complete event contains a string such as "******" for one or both counters.
  4. The session usage tracker aggregates the value with += without validating its type. JavaScript then concatenates the string into the session total.
  5. session.shutdown.data.codeChanges.linesAdded or linesRemoved is persisted as a string.
  6. The next session.resume rejects events.jsonl because ShutdownCodeChanges requires an integer (expected i64).

The session's conversation and tool results remain present, but the entire session can no longer be resumed because of diagnostic usage metadata.

Affected versions

  • @github/copilot: 1.0.73 (package build commit 98003c0)
  • @github/copilot-sdk: 1.0.7
  • Runtime: Linux container, Node.js 20

Why this appears to happen

The external tool result contract permits opaque JSON under toolTelemetry. The shutdown event contract is stricter and requires nonnegative integers for codeChanges.linesAdded and linesRemoved.

The 1.0.73 runtime's usage aggregation effectively performs:

aggregate.linesAdded += toolTelemetry.metrics.linesAdded ?? 0;
aggregate.linesRemoved += toolTelemetry.metrics.linesRemoved ?? 0;

If a persisted tool event contains a masked string, numeric addition becomes string concatenation. The resulting shutdown record violates the runtime's own resume schema.

The exact component that replaces the original integer with the masked string appears to be between post-tool-hook completion and persisted tool.execution_complete event creation. The tool result itself and the unmodified post-tool-hook input contain integers.

Expected behavior

  • linesAdded and linesRemoved remain nonnegative integers throughout tool-event persistence.
  • Invalid diagnostic metrics never make the conversation unresumable.
  • Resume tolerates or repairs malformed diagnostic-only usage fields rather than rejecting the complete session.

Suggested fixes

  1. Validate code-change counters before writing tool.execution_complete.
  2. In usage aggregation, add values only when Number.isSafeInteger(value) && value >= 0.
  3. During event replay, ignore malformed code-change metrics instead of concatenating them.
  4. Before writing session.shutdown, assert that aggregate counters satisfy ShutdownCodeChanges.
  5. Add a backward-compatible repair path for already persisted sessions, treating unrecoverable masked counters as zero or recomputing them from valid events.
  6. Consider skip-and-warn behavior for schema-invalid diagnostic fields so one metric cannot invalidate a complete session.

Workaround

With the CLI stopped, back up events.jsonl, then atomically sanitize both locations while preserving one JSON object per LF-terminated line:

  • In tool.execution_complete, replace non-integer or negative toolTelemetry.metrics.linesAdded / linesRemoved with 0 (or remove those metric keys).
  • In session.shutdown, replace invalid codeChanges.linesAdded / linesRemoved with 0, or recompute them from valid tool events.

Both locations must be repaired; fixing only the shutdown record can allow replay of an earlier malformed tool event to poison a later shutdown again.

Related issues

This appears to be another writer-versus-validator mismatch in the same family as:

No raw session logs, user content, or internal identifiers are included in this report.

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:sessionsSession management, resume, history, session picker, and session state

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions