Skip to content

Stop Copilot retries on tool-call ID schema errors - #65761

Merged
pelikhan merged 2 commits into
mainfrom
copilot/fix-copilot-harness-continue-retry
Oct 5, 2026
Merged

pelikhan merged 2 commits into
mainfrom
copilot/fix-copilot-harness-continue-retry

Conversation

Copilot AI commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

A 400 Invalid 'input[N].id' error caused by a ctc_call_... ID being rejected in favor of an fc ID was treated as partial execution. Retrying with --continue reused the malformed conversation state and exhausted the retry budget.

  • Failure handling
    • Classify this specific ID mismatch separately from other HTTP 400 errors.
    • Stop retrying rather than resume the poisoned session or restart after partial execution.
  • Regression coverage
    • Cover the mismatch on both the initial attempt and an attempt resumed after partial execution.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix copilot harness continue retry failing with 400 error Stop Copilot retries on tool-call ID schema errors Oct 5, 2026
Copilot AI requested a review from pelikhan October 5, 2026 05:01
@pelikhan
pelikhan marked this pull request as ready for review October 5, 2026 05:05
Copilot AI balanced review requested due to automatic review settings October 5, 2026 05:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The targeted non-transient failure is correctly handled and covered across both required retry scenarios.

Review effort: Balanced
Findings: None

What changed in this PR

Stops futile Copilot retries when malformed tool-call IDs poison session state.

Changes:

  • Detects and classifies the specific ctc_call_/fc schema mismatch.
  • Fails immediately without resuming or restarting.
  • Adds initial and resumed-attempt regression coverage.
File Description
actions/​setup/​js/​copilot_harness.cjs Adds terminal error detection and handling.
actions/​setup/​js/​copilot_harness.test.cjs Tests detection, classification, and retry prevention.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

✅ Ponytail Reviewer completed successfully!

Lean already. Ship.

Generated by Ponytail Reviewer for #65761

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

🔍 Design Decision Gate 🏗️ is checking for design decision records on this pull request...

@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

✅ PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

✅ Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel skipped because pre-fetch PR data was unavailable: unable to fetch test file diff

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Comment Memory

Peek at saved memory (pr-code-quality-reviewer)
reviewed_at: 2026-10-05T14:20:04Z
review_event: REQUEST_CHANGES
top_themes:
  - missing external plumbing for tool_call_id_schema_error
files_reviewed:
  - actions/setup/js/copilot_harness.cjs
  - actions/setup/js/copilot_harness.test.cjs
comment_count: 1

Note

This comment is managed by comment memory.

Expand the saved memory block to view or edit the persistent context for this thread.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 47 AIC · ⌖ 7.14 AIC · ⊞ 19.4K · ◷
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes

The retry guard looks right, but the new tool_call_id_schema_error classification never leaves the harness: downstream outputs still expose only the generic HTTP 400 bucket, so the separate-classification part of the fix is still incomplete.

Blocking theme
  • The added branch stops retries locally, but detectCopilotErrors, the aggregated detectedCopilotErrors state, and writeCopilotOutputs still have no tool_call_id_schema_error field, so workflow consumers cannot distinguish this condition from any other http_400_response_error.

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 47 AIC · ⌖ 7.14 AIC · ⊞ 19.4K
Comment /review to run again

isMCPPolicy,
isModelNotSupported,
isHTTP400ResponseError: hasHTTP400ResponseError,
isToolCallIdSchemaError: hasToolCallIdSchemaError,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only creates an internal failure class; the workflow outputs still collapse the same error into http_400_response_error, so downstream steps cannot actually distinguish or handle the new non-retryable case.

💡 Why this blocks

The PR description says this mismatch should be classified separately from other HTTP 400s, but the new flag never gets wired into the existing detection/output plumbing (detectCopilotErrors, detectedCopilotErrors, writeCopilotOutputs). Right now anything consuming $GITHUB_OUTPUT / needs.agent.outputs.* still only sees the generic http_400_response_error=true, so the new classification is invisible outside this local retry branch.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — this is a clean, well-targeted fix with solid regression coverage. Approving with two minor suggestions.

📋 Key Themes & Highlights

Key Themes

  • Root cause correctly addressed: the fix distinguishes the ctc_call_... → fc ID-schema mismatch from generic partial_execution/HTTP 400 handling and stops retrying instead of resuming poisoned --continue state — exactly matching the root cause described in #65756.
  • Pattern specificity vs. generality: TOOL_CALL_ID_SCHEMA_ERROR_PATTERN hardcodes the exact observed prefixes (ctc_call_ → fc). A more general "ID prefix/schema mismatch" pattern would be more robust to future variants of the same underlying bug class (see inline comment).
  • Cross-file pattern sync: other classification patterns (HTTP_400_RESPONSE_ERROR_PATTERN) carry an explicit "keep in sync with detect_agent_errors.cjs" comment; the new pattern has no such note. Confirmed this gap also exists for the pre-existing null_type_tool_call class, so it's consistent with precedent rather than a new regression — flagged for awareness only.

Positive Highlights

  • ✅ Regression tests cover both the first-attempt failure and the attempt resumed after partial execution (it.each([false, true])), which is exactly the scenario from the linked issue reproduction.
  • ✅ New check is placed correctly ahead of the generic hasHTTP400ResponseError branch so it takes precedence without disturbing existing HTTP-400/null-type-tool-call handling.
  • ✅ Doc comment block at the top of the file is updated to describe the new non-retryable class alongside the existing null-type-tool-call precedent — good practice for a file with many special-cased failure modes.

@copilot please address the review comments above.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 76.9 AIC · ⌖ 13.6 AIC · ⊞ 10K
Comment /matt to run again

Comments that could not be inline-anchored

actions/setup/js/copilot_harness.cjs:198

[/diagnosing-bugs] This pattern hardcodes the exact ctc_call_ → fc prefix pair from the one observed error. If the backend emits the same ID-schema-mismatch shape with a different expected prefix (e.g. a future model/tool-call ID scheme), this regex silently falls through to the generic HTTP_400_RESPONSE_ERROR_PATTERN/partial_execution path and regresses to the exact retry-storm bug this PR fixes.

<details>
<summary>💡 Suggested generalization</summary>

Consider loosening the seco…

actions/setup/js/copilot_harness.cjs:130

[/codebase-design] HTTP_400_RESPONSE_ERROR_PATTERN carries a // NOTE: keep in sync with ... detect_agent_errors.cjs comment (mirrored in agent_error_patterns.cjs), signaling that these failure-classification patterns are intentionally duplicated/tracked across files for the top-level error-detection surface. The new TOOL_CALL_ID_SCHEMA_ERROR_PATTERN has no such cross-reference, so there's no explicit record of whether it should also be surfaced there for consistent top-level failure…

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed with a correctness/security focus (backend Node.js harness change, not UI — Impeccable modes don't apply).

Findings: None blocking. The new TOOL_CALL_ID_SCHEMA_ERROR_PATTERN / isToolCallIdSchemaError correctly matches the ctc_call_... → fc ID-mismatch error, is placed above the generic hasHTTP400ResponseError check in the retry decision tree (so it takes precedence and the harness now stops immediately instead of burning retries on poisoned --continue state), and classifyCopilotFailure orders it consistently with other non-retryable classes. Verified the regex and classification manually by requiring the module and exercising both functions directly (vitest isn't installable in this sandbox — no network access to fetch deps — so I validated via direct node -e calls against the exported functions instead).

Tests cover both the direct-failure and partial-execution-then-schema-error paths, matching the issue's reproduction scenario (#65756). Documentation comment at the top of the file was updated accordingly.

No actionable changes requested.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · copilot · sonnet50 · 73.6 AIC · ⌖ 14 AIC · ⊞ 8.1K

@pelikhan
pelikhan merged commit 7bcfc7c into main Oct 5, 2026
68 of 69 checks passed
@pelikhan
pelikhan deleted the copilot/fix-copilot-harness-continue-retry branch October 5, 2026 14:31
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.91.0

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

copilot harness: --continue retry reproduces same 400 'Expected an ID that begins with fc' error on every attempt, exhausting all retries

3 participants