@@ -625,6 +625,7 @@ func runAgent(ctx context.Context, agentName, fullsendDir, outputBase, targetRep
625625 // registered before the post-script and cleanup defers so that — by LIFO
626626 // order — it runs last and the summary captures the whole run.
627627 var lastExitCode int
628+ var transcriptErrorOverride bool
628629 rec := telemetry .New (runDir , wTraceID , rootSpanID , agentName , workItemID , runStart )
629630 defer func () { rec .Finalize (telemetryExitCode (lastExitCode , runErr )) }()
630631
@@ -666,6 +667,10 @@ func runAgent(ctx context.Context, agentName, fullsendDir, outputBase, targetRep
666667 printer .StepWarn ("Skipping post-script: agent run failed" )
667668 return
668669 }
670+ if transcriptErrorOverride {
671+ printer .StepWarn ("Skipping post-script: agent reported error via transcript" )
672+ return
673+ }
669674 postStart := time .Now ()
670675 printer .StepStart ("Running post-script: " + h .PostScript )
671676 postCmd := exec .Command (h .PostScript )
@@ -950,6 +955,7 @@ func runAgent(ctx context.Context, agentName, fullsendDir, outputBase, targetRep
950955
951956 for iteration := 1 ; iteration <= maxIterations ; iteration ++ {
952957 runCount = iteration
958+ transcriptErrorOverride = false
953959
954960 // Each iteration gets its own subdirectory for output and transcripts.
955961 iterDir := filepath .Join (runDir , fmt .Sprintf ("iteration-%d" , iteration ))
@@ -1019,12 +1025,26 @@ func runAgent(ctx context.Context, agentName, fullsendDir, outputBase, targetRep
10191025 }
10201026 lastExitCode = exitCode
10211027
1028+ // Check the tee'd output.jsonl for is_error:true result events.
1029+ // Claude Code may exit 0 on API/infrastructure failures (e.g.,
1030+ // invalid_grant, quota exhaustion) while setting is_error:true in
1031+ // the transcript. Treat these as failures so downstream gating
1032+ // (transcript surfacing, post-script skip) can act. See #2786.
1033+ if exitCode == 0 {
1034+ outputJSONL := filepath .Join (iterDir , "output.jsonl" )
1035+ if te , ok := tx .ParseTranscriptFile (outputJSONL ); ok && te .IsError {
1036+ printer .StepWarn (fmt .Sprintf ("Agent exited with code 0 but transcript contains error: %s" , te .ErrorMessage ))
1037+ lastExitCode = 1
1038+ transcriptErrorOverride = true
1039+ }
1040+ }
1041+
10221042 printer .Blank ()
10231043 // Non-zero exit is a warning, not a failure — the validation loop is the success gate.
1024- if exitCode == 0 {
1044+ if lastExitCode == 0 {
10251045 printer .StepDone (fmt .Sprintf ("Agent exited with code %d (%.1fs)" , exitCode , time .Since (agentStart ).Seconds ()))
10261046 } else {
1027- printer .StepWarn (fmt .Sprintf ("Agent exited with code %d" , exitCode ))
1047+ printer .StepWarn (fmt .Sprintf ("Agent exited with code %d" , lastExitCode ))
10281048 }
10291049
10301050 // 9b. Extract output files.
@@ -1114,16 +1134,15 @@ func runAgent(ctx context.Context, agentName, fullsendDir, outputBase, targetRep
11141134 rec .SetModel (aggMetrics .Model )
11151135
11161136 // 9e-bis. Surface transcript errors in workflow logs (GitHub Actions).
1117- // When the agent exits non-zero, parse transcript JSONL files and emit
1118- // ::error:: annotations so operators can diagnose failures without
1119- // downloading artifacts. See #704.
1120- if lastExitCode != 0 {
1121- lastIterDir := filepath .Join (runDir , fmt .Sprintf ("iteration-%d" , runCount ))
1122- lastTranscriptDir := filepath .Join (lastIterDir , "transcripts" )
1123- if errorSummaries := tx .ParseTranscriptErrors (lastTranscriptDir ); len (errorSummaries ) > 0 {
1124- printer .StepWarn (fmt .Sprintf ("Found %d transcript error(s) — emitting to workflow log" , len (errorSummaries )))
1125- tx .EmitTranscriptErrors (os .Stderr , errorSummaries )
1126- }
1137+ // Parse transcript JSONL files and emit ::error:: annotations so operators
1138+ // can diagnose failures without downloading artifacts. This runs
1139+ // regardless of exit code because Claude Code may exit 0 with
1140+ // is_error:true on API/infrastructure failures. See #704, #2786.
1141+ lastIterDir := filepath .Join (runDir , fmt .Sprintf ("iteration-%d" , runCount ))
1142+ lastTranscriptDir := filepath .Join (lastIterDir , "transcripts" )
1143+ if errorSummaries := tx .ParseTranscriptErrors (lastTranscriptDir ); len (errorSummaries ) > 0 {
1144+ printer .StepWarn (fmt .Sprintf ("Found %d transcript error(s) — emitting to workflow log" , len (errorSummaries )))
1145+ tx .EmitTranscriptErrors (os .Stderr , errorSummaries )
11271146 }
11281147
11291148 // 9f. Post-agent output scan — redact secrets from extracted output.
0 commit comments