Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Fix default failure-cause misclassification as prompt_exhaustion
buildEmptyOutputOutcome() fell back to "prompt_exhaustion" whenever no
PROMPT_EXHAUSTION_ERROR_CATEGORIES, REQUEST_REJECTION_ERROR_CATEGORIES,
or 5xx engine-outage evidence was found. Absence of evidence is not
evidence of token/invocation-limit exhaustion, so most
missing_terminal_safe_output incompletions (no matched category, 0
retries observed) were mislabeled as prompt_exhaustion. This inflated
the prompt_exhaustion failure-issue cluster (#66817) across
unrelated workflows such as Avenger.

Classify the unmatched case as a new "unknown" failure cause instead of
defaulting to prompt_exhaustion, with a matching issue-title mapping.

Fixes #66817

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
  • Loading branch information
pelikhan and Copilot committed Oct 10, 2026
commit 30b79f66db3687a2cf4838db3e113f4f7586fbfe
6 changes: 3 additions & 3 deletions actions/setup/js/collect_ndjson_output.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ const incompleteOutput = JSON.stringify({
{
type: "report_incomplete",
reason: "missing_terminal_safe_output",
failureCause: "prompt_exhaustion",
failureCause: "unknown",
retryCount: 0,
details: "Agent finished without emitting a terminal safe output; task completion could not be confirmed.\nFailure classification: prompt_exhaustion\nRetry attempts observed: 0",
details: "Agent finished without emitting a terminal safe output; task completion could not be confirmed.\nFailure classification: unknown\nRetry attempts observed: 0",
},
],
errors: [],
collectorEmptyOutputCause: "missing_terminal_safe_output",
collectorFailureCause: "prompt_exhaustion",
collectorFailureCause: "unknown",
collectorRetryCount: 0,
});
describe("collect_ndjson_output.cjs", () => {
Expand Down
5 changes: 4 additions & 1 deletion actions/setup/js/empty_output_outcome.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const EMPTY_OUTPUT_FAILURE_CAUSES = Object.freeze({
engine_outage: "experienced an engine outage",
request_rejection: "had a request rejected",
prompt_exhaustion: "exhausted its prompt",
unknown: "finished without a clear failure cause",
});

const PROMPT_EXHAUSTION_ERROR_CATEGORIES = new Set(["effective_tokens_limit_exceeded", "invocation_cap_exceeded"]);
Expand Down Expand Up @@ -166,7 +167,9 @@ function buildEmptyOutputOutcome(errors, rootDir = "/tmp/gh-aw") {
const isPromptExhaustion = executionCategories.some(category => PROMPT_EXHAUSTION_ERROR_CATEGORIES.has(category));
const isRequestRejection = ["invalid_safe_outputs", "safeoutputs_cli_error"].includes(reason) || executionCategories.some(category => REQUEST_REJECTION_ERROR_CATEGORIES.has(category)) || errorCodes.some(code => code >= 400 && code < 500);
const isEngineOutage = reason === "engine_driver_failure" || executionCategories.some(category => ["agentic_engine_timeout", "capi_server_error", "sandbox_runtime_crash"].includes(category)) || errorCodes.some(code => code >= 500);
const failureCause = isPromptExhaustion ? "prompt_exhaustion" : isRequestRejection ? "request_rejection" : isEngineOutage ? "engine_outage" : "prompt_exhaustion";
// Absence of evidence is not evidence of prompt exhaustion: without a matched category
// or status code, classify as "unknown" instead of guessing prompt_exhaustion.
const failureCause = isPromptExhaustion ? "prompt_exhaustion" : isRequestRejection ? "request_rejection" : isEngineOutage ? "engine_outage" : "unknown";
Comment thread
pelikhan marked this conversation as resolved.
Resolved
const retryEvents = events.filter(event => event.type === "claude.api_retry" || (event.type === "system" && event.data.subtype === "api_retry"));
const harnessRetryLines = stdio.split(/\r?\n/).filter(line => /^\[(?:copilot|claude|codex)-harness\].*\bretrying\b/i.test(line));
const harnessRetryCount = harnessRetryLines.length;
Expand Down
4 changes: 2 additions & 2 deletions actions/setup/js/empty_output_outcome.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ describe("empty output outcome", () => {
expect(buildEmptyOutputOutcome([], rootDir)).toEqual({
type: "report_incomplete",
reason: "missing_terminal_safe_output",
failureCause: "prompt_exhaustion",
failureCause: "unknown",
retryCount: 0,
details: "Agent finished without emitting a terminal safe output; task completion could not be confirmed.\nFailure classification: prompt_exhaustion\nRetry attempts observed: 0",
details: "Agent finished without emitting a terminal safe output; task completion could not be confirmed.\nFailure classification: unknown\nRetry attempts observed: 0",
});
});

Expand Down
1 change: 1 addition & 0 deletions actions/setup/js/handle_agent_failure.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ describe("handle_agent_failure", () => {
["engine_outage", "[aw] Test Workflow experienced an engine outage"],
["request_rejection", "[aw] Test Workflow had a request rejected"],
["prompt_exhaustion", "[aw] Test Workflow exhausted its prompt"],
["unknown", "[aw] Test Workflow finished without a clear failure cause"],
])("uses the classified terminal-output cause %s in the issue title", (terminalOutputFailureCause, title) => {
expect(buildFailureIssueTitle({ ...baseOptions, terminalOutputFailureCause })).toBe(title);
});
Expand Down
Loading