-
Notifications
You must be signed in to change notification settings - Fork 429
Prioritize max-ai-credits exhaustion over engine HTTP 429 classification in failure reporting #38022
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prioritize max-ai-credits exhaustion over engine HTTP 429 classification in failure reporting #38022
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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())) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suppression is incomplete: raw 429 log text still surfaces in the fallback tail output, potentially undermining the intent. 💡 Details and suggested approachWhen The user then sees:
This is a partial suppression only. Compare with the // mirrors the hasToolDenialsExceeded pattern
const engineFailureContext =
agentConclusion === "failure" && !hasToolDenialsExceeded && !maxAICreditsExceeded
? buildEngineFailureContext()
: "";If partial suppression (raw diagnostics visible, guided 429 remediation hidden) is intentional, a short comment at the call site documenting that decision would prevent future engineers from tightening the assertion by accident. |
||
| 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); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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", () => { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [/tdd] The test covers 💡 Suggested test to add nearbyit("returns engine 429 context when suppressEngineRateLimit429 is false and signal is present", () => {
fs.writeFileSync(stdioLogPath, "...CAPIError: 429...\n");
const result = buildEngineFailureContext({ suppressEngineRateLimit429: false });
expect(result).toContain("Engine Rate Limited (HTTP 429)");
});This guards against a future change that accidentally hard-codes the flag to |
||
| 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"); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [/tdd] The 💡 SuggestionIf a constant (or an exported string) is already used elsewhere for this heading, reference it here. Otherwise, consider asserting on a more stable/specific sub-string from the fallback context body rather than a section heading. For example: // Instead of: expect(result).toContain("Engine Failure");
// Prefer something derived from a known constant, e.g.:
const { ENGINE_FAILURE_HEADING } = require("./constants");
expect(result).toContain(ENGINE_FAILURE_HEADING);At minimum, add a brief comment explaining why the fallback falls through to this template when the 429 branch is suppressed, so future readers know the assertion is intentional.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test asserts the right output header, but does not verify whether the raw 429 log text is absent from the fallback result. 💡 DetailsWith If the intent of the suppression is to remove all 429 noise from the failure report, the following assertion would catch a regression: expect(result).not.toContain("CAPIError");If the raw diagnostics are intentionally kept visible (only the actionable guided 429 section is suppressed), add a brief comment to the test explaining this so the assertion boundary is clear to future readers. |
||
| }); | ||
|
|
||
| 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( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing
@paramJSDoc for the newoptionsparameter.💡 Suggested addition
The JSDoc block above documents
@returnsbut does not describe the newoptionsparameter or itssuppressEngineRateLimit429key. In a file this size, undocumented optional object params are easy to misuse.