Skip to content

Fix Claude harness retrying invalid-JSON body error with --continue instead of a fresh run - #51793

Merged
pelikhan merged 3 commits into
mainfrom
copilot/aw-failures-fix-invalid-json-request
Aug 10, 2026
Merged

Fix Claude harness retrying invalid-JSON body error with --continue instead of a fresh run#51793
pelikhan merged 3 commits into
mainfrom
copilot/aw-failures-fix-invalid-json-request

Conversation

Copilot AI commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

A permission_denied on a compound Bash command is sometimes followed, on the very next turn, by the Anthropic API rejecting the request with 400 The request body is not valid JSON. The harness's failure classifier had no bucket for this signature, so it fell through to the generic "partial execution" path and retried with --continue — resending the same corrupted on-disk session state and reproducing the identical error, burning the run's last retry.

Changes

  • New classifier: added INVALID_JSON_BODY_ERROR_PATTERN / isInvalidJsonBodyError() in actions/setup/js/claude_harness.cjs to recognize "request body is not valid JSON" in the CLI output.
  • Retry behavior: when this signature is detected, the harness forces the next attempt to be a fresh run instead of --continue, and permanently disables --continue for the rest of the driver invocation — same treatment as the existing isNoDeferredMarkerError path, since resuming would just resend the same broken session state.
  • Diagnostics: the new classification is logged alongside the existing ones (isOverloadedError, isRateLimitError, etc.) for easier triage of future occurrences.
  • Tests: added coverage for isInvalidJsonBodyError in claude_harness.test.cjs, including the true-to-log-format scenario from the failing run.

Note: the underlying serialization bug lives inside the third-party Claude Code CLI binary, outside this repo, so it can't be patched directly — this change is the harness-side mitigation so a single serialization glitch doesn't consume the whole retry budget.

// before: falls through to generic "partial execution" retry, uses --continue
// after:
if (isInvalidJsonBody && attempt < maxRetries && result.hasOutput) {
  useContinueOnRetry = false;
  continueDisabledPermanently = true;
  log(`attempt ${attempt + 1}: invalid JSON request body (transport-level serialization bug, likely following a permission_denied) — retrying as fresh run...`);
  continue;
}

…resh run

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix Claude harness sending invalid-JSON request body after retry Fix Claude harness retrying invalid-JSON body error with --continue instead of a fresh run Aug 10, 2026
Copilot AI requested a review from pelikhan August 10, 2026 13:51
@pelikhan
pelikhan marked this pull request as ready for review August 10, 2026 15:33
Copilot AI balanced review requested due to automatic review settings August 10, 2026 15:33

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.

Pull request overview

Adds a Claude harness mitigation for invalid-JSON API failures caused by corrupted resumable sessions.

Changes:

  • Detects invalid-JSON request-body errors.
  • Forces fresh retries and permanently disables --continue.
  • Adds classifier-focused tests.
Show a summary per file
File Description
actions/setup/js/claude_harness.cjs Implements detection, diagnostics, and fresh-run retry behavior.
actions/setup/js/claude_harness.test.cjs Tests invalid-JSON error classification.

Review details

Tip

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

  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread actions/setup/js/claude_harness.cjs Outdated
Comment on lines +590 to +594
if (isInvalidJsonBody && attempt < maxRetries && result.hasOutput) {
useContinueOnRetry = false;
continueDisabledPermanently = true;
log(`attempt ${attempt + 1}: invalid JSON request body (transport-level serialization bug, likely following a permission_denied) — retrying as fresh run (--continue disabled permanently, attempt ${attempt + 2}/${maxRetries + 1})`);
continue;
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • api.individual.githubcopilot.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "api.individual.githubcopilot.com"

See Network Configuration for more information.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Ponytail Reviewer completed successfully!

Generated by Ponytail Reviewer for #51793

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Aug 10, 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 Aug 10, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #51793 does not have the 'implementation' label and has 0 new lines of code in business logic directories (threshold: 100).

🏗️ ADR gate enforced by Design Decision Gate 🏗️

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

The fix is correct and well-structured. The isInvalidJsonBody check correctly precedes the generic --continue retry, continueDisabledPermanently prevents future regressions, and the result.hasOutput guard is consistent with other branches. Tests cover the classifier thoroughly. The existing inline comment about adding integration/retry-policy tests is the only outstanding suggestion.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 22.1 AIC · ⌖ 7.07 AIC · ⊞ 5.4K

@github-actions

Copy link
Copy Markdown
Contributor

Test Quality Sentinel 🧪 — PR #51793

Test Quality Score: 100/100 ✅ Excellent

Summary

This PR adds comprehensive test coverage for the new isInvalidJsonBodyError() classifier function, which detects transport-level JSON serialization bugs following permission_denied errors in the Claude Code harness.

Coverage Quality:

  • 8 new test cases added to claude_harness.test.cjs
  • Test inflation ratio: 36 test lines : 43 production lines = 0.84:1 (healthy ✅)
  • All 8 tests are design tests (verify the function's behavioral contract)
  • 0% implementation tests (threshold: 30%) ✅
  • No mock library violations
  • No guideline violations

Test Breakdown

Test Name Type Value Details
canonical Anthropic 400 message design_test high_value Happy path: exact API error pattern
mixed-case variant design_test high_value Edge case: case-insensitive matching
error after permission_denied design_test high_value Real-world scenario from failing run #51790
false: overloaded_error design_test high_value False-positive prevention (other error class)
false: rate_limit_error design_test high_value False-positive prevention (other error class)
false: empty string design_test high_value Boundary case
false: success result design_test high_value False-positive prevention (success path)

Quality Signals

Design invariant protection: All 8 tests verify that the function correctly distinguishes "request body is not valid JSON" from other known error signatures (overloaded_error, rate_limit_error, success results).

Real-world coverage: Test case 3 directly mirrors the failing scenario from issue #51790 (permission_denied → corrupted JSON → 400 error).

False-positive prevention: 5 negative assertions ensure the classifier doesn't misfire on other error types — critical for a branching control-flow classifier.

Edge case handling: Case-insensitivity check ensures robustness across API output variations.

No test inflation: 36 test lines for 43 lines of production code is a healthy 0.84:1 ratio.

Pattern classifier correctness: Simple, clear assertions on regex behavior — no hidden implementation details or implicit mocking.


Status: ✅ APPROVED — Test Quality Sentinel analysis complete.

🧪 Test quality analysis by Test Quality Sentinel · haiku45 · 15.8 AIC · ⌖ 3.28 AIC · ⊞ 7.6K ·
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.

✅ Test Quality Sentinel: 100/100. All 8 tests are design tests verifying the behavioral contract of isInvalidJsonBodyError(). 0% implementation tests (threshold: 30%). No violations.

@github-actions github-actions Bot mentioned this pull request Aug 10, 2026

@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 using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 36.4 AIC · ⌖ 7.42 AIC · ⊞ 7.1K
Comment /matt to run again

Comment thread actions/setup/js/claude_harness.cjs Outdated
// command. Retrying with --continue would resend the same corrupted on-disk
// session state and reproduce the identical error, so force a fresh run and
// permanently disable --continue for the remainder of this driver invocation.
if (isInvalidJsonBody && attempt < maxRetries && result.hasOutput) {

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.

[/diagnosing-bugs] When isInvalidJsonBody is true but attempt >= maxRetries or !result.hasOutput, the guard is skipped and execution falls through to the generic partial-execution branch — which may still attempt a --continue retry, exactly the bug this PR intends to prevent.

💡 Suggested fix — mirror the isNoDeferredMarker pattern
if (isInvalidJsonBody) {
  if (attempt < maxRetries && result.hasOutput) {
    useContinueOnRetry = false;
    continueDisabledPermanently = true;
    log(`attempt ${attempt + 1}: invalid JSON request body ... retrying as fresh run`);
    continue;
  }
  // exhausted retries or no output — stop; do NOT fall through to generic branch
  break;
}

isNoDeferredMarker already does this: it has an explicit break for the non-retriable sub-case (line ~582). The new block lacks that safeguard.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in fe68e68: the isInvalidJsonBody block now mirrors isNoDeferredMarker — it has an explicit break when retries are exhausted or there's no output, so it no longer falls through to the generic partial-execution --continue retry. Also added a stubbed harness-level regression test covering this retry-policy 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.

Generated by ✂️ Ponytail Reviewer for #51793 · auto · 19.9 AIC · ⌖ 3.28 AIC · ⊞ 6.8K
Comment /ponytail to run again

continueDisabledPermanently = true;
log(`attempt ${attempt + 1}: invalid JSON request body (transport-level serialization bug, likely following a permission_denied) — retrying as fresh run (--continue disabled permanently, attempt ${attempt + 2}/${maxRetries + 1})`);
continue;
}

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.

L574-595: shrink: isNoDeferredMarker and isInvalidJsonBody blocks are identical (same reset + log + continue). if ((isNoDeferredMarker || isInvalidJsonBody) && attempt < maxRetries && result.hasOutput), ~10 fewer lines.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
@pelikhan
pelikhan merged commit 965f4a3 into main Aug 10, 2026
5 of 6 checks passed
@pelikhan
pelikhan deleted the copilot/aw-failures-fix-invalid-json-request branch August 10, 2026 21:00
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.86.2

DemidovVladimir added a commit to moleculeprotocol/docs that referenced this pull request Aug 14, 2026
…e, close IP-2864 AC gaps

- Re-pin v0.85.4 -> v0.86.2 and record the pin + its reason in the workflow
  header (IP-2864 AC). v0.86.2 carries the Claude-harness retry fix
  (github/gh-aw#51793) whose trigger scenario is exactly this workflow's
  engine + strict bash allow-list, plus enforced secret redaction in step
  summaries and patch artifacts (#50777/#50778). Verified behaviour-neutral
  for the DOCS-2 write strategy: safe-output schemas and the
  close_older_pull_requests handler are unchanged between the two versions.

- Release notes now travel hub-side, not in the dispatch payload: a
  pre-agent step mints a token from the read-only source App (Releases API
  is covered by contents: read - no permission change) and writes the body
  to ./source/RELEASE_NOTES.md. The payload lands in a public repo's
  workflow run, and source release bodies now carry real content naming
  private infrastructure (desci-infra 2.0.0, 2026-08-12). Contract change
  lands in desci-infra#675 in the same breath.

- Add the claude-code-action fallback note (IP-2864 AC), flag it as
  unvalidated for repository_dispatch.

- Relevance gate: add docs/service-auth.md (mapped for authentication and
  roles pages, previously unreachable); document that the gate is a
  deliberate triggering subset of the map, with the ride-along rows named
  in the knowledge base.

- Knowledge base: account for the previously unmapped pages
  (technical-deep-dive/onchain-lab.md, module-registry/**, data/README.md,
  and the orphan api-reference/IPNFT-api.md) in the out-of-scope list.

- stop-after refreshed at compile: expiry is now 2026-08-28 10:16:52. If
  this PR has not merged by then, run `gh aw compile --refresh-stop-time`
  again before merging or the pilot arrives expired.

Recompiled with gh-aw v0.86.2; `gh aw validate --strict` and `gh aw lint`
both clean; agent job verified still contents: read only, bots allow-list
and max-ai-credits unchanged in the regenerated lock.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[aw-failures] [P1] Fix Claude harness sending invalid-JSON request body after a permission_denied retry

3 participants