diff --git a/actions/setup/js/handle_agent_failure.cjs b/actions/setup/js/handle_agent_failure.cjs index 2d3d8e2bbf5..6c918a5d2f4 100644 --- a/actions/setup/js/handle_agent_failure.cjs +++ b/actions/setup/js/handle_agent_failure.cjs @@ -1827,7 +1827,8 @@ function hasAgentTerminalReasonCompleted() { * The log file is available in the conclusion job after the agent artifact is downloaded. * @returns {string} Formatted context string, or empty string if no engine failure found */ -function buildEngineFailureContext() { +function buildEngineFailureContext(options = {}) { + const suppressEngineRateLimit429 = options.suppressEngineRateLimit429 === true; // Derive agent-stdio.log path from the agent output file path (same directory) const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT; const stdioLogPath = agentOutputFile ? path.join(path.dirname(agentOutputFile), "agent-stdio.log") : "/tmp/gh-aw/agent-stdio.log"; @@ -1858,7 +1859,7 @@ function buildEngineFailureContext() { return ""; } - if (hasEngineRateLimit429Signal(logContent) || hasEngineRateLimit429InOTELMirror()) { + if (!suppressEngineRateLimit429 && (hasEngineRateLimit429Signal(logContent) || hasEngineRateLimit429InOTELMirror())) { core.info("Detected engine HTTP 429/rate-limit signal — using dedicated context message"); return buildEngineRateLimit429Context(engineLabel); } @@ -2717,7 +2718,7 @@ async function main() { // Suppress when tool-denials-exceeded is present: the engine termination is a // direct consequence of the SDK hitting the denial threshold, so the tool-denials // context is the more actionable signal. - const engineFailureContext = agentConclusion === "failure" && !hasToolDenialsExceeded ? buildEngineFailureContext() : ""; + const engineFailureContext = agentConclusion === "failure" && !hasToolDenialsExceeded ? buildEngineFailureContext({ suppressEngineRateLimit429: maxAICreditsExceeded }) : ""; // Build timeout context const timeoutContext = buildTimeoutContext(isTimedOut, timeoutMinutes); @@ -2941,7 +2942,7 @@ async function main() { // Suppress when tool-denials-exceeded is present: the engine termination is a // direct consequence of the SDK hitting the denial threshold, so the tool-denials // context is the more actionable signal. - const engineFailureContext = agentConclusion === "failure" && !hasToolDenialsExceeded ? buildEngineFailureContext() : ""; + const engineFailureContext = agentConclusion === "failure" && !hasToolDenialsExceeded ? buildEngineFailureContext({ suppressEngineRateLimit429: maxAICreditsExceeded }) : ""; // Build timeout context const timeoutContext = buildTimeoutContext(isTimedOut, timeoutMinutes); diff --git a/actions/setup/js/handle_agent_failure.test.cjs b/actions/setup/js/handle_agent_failure.test.cjs index 017844c5f79..1a16e4f69c9 100644 --- a/actions/setup/js/handle_agent_failure.test.cjs +++ b/actions/setup/js/handle_agent_failure.test.cjs @@ -1490,6 +1490,13 @@ describe("handle_agent_failure", () => { expect(result).not.toContain("Last agent output"); }); + it("suppresses engine 429 context when max-ai-credits-exceeded takes precedence", () => { + fs.writeFileSync(stdioLogPath, "Failed to get response from the AI model; retried 5 times. Last error: CAPIError: 429 429 Sorry, you've exceeded your rate limit for utility models.\n"); + const result = buildEngineFailureContext({ suppressEngineRateLimit429: true }); + expect(result).not.toContain("Engine Rate Limited (HTTP 429)"); + expect(result).toContain("Engine Failure"); + }); + it("returns dedicated context when 429/rate-limit is only present in OTLP mirror", () => { fs.writeFileSync(stdioLogPath, "Agent terminated unexpectedly without clear error details\n"); fs.writeFileSync(