fix(agents): avoid classifying reasoning-required errors as context overflow (#24593)

* Agents: exclude reasoning-required errors from overflow detection

* Tests: cover reasoning-required overflow classification guard

* Tests: format reasoning-required endpoint errors
This commit is contained in:
Vincent Koc
2026-02-23 10:38:49 -05:00
committed by GitHub
parent 652099cd5c
commit 4f340b8812
3 changed files with 59 additions and 0 deletions

View File

@@ -35,6 +35,15 @@ describe("formatAssistantErrorText", () => {
);
expect(formatAssistantErrorText(msg)).toContain("Context overflow");
});
it("returns a reasoning-required message for mandatory reasoning endpoint errors", () => {
const msg = makeAssistantError(
"400 Reasoning is mandatory for this endpoint and cannot be disabled.",
);
const result = formatAssistantErrorText(msg);
expect(result).toContain("Reasoning is required");
expect(result).toContain("/think minimal");
expect(result).not.toContain("Context overflow");
});
it("returns a friendly message for Anthropic role ordering", () => {
const msg = makeAssistantError('messages: roles must alternate between "user" and "assistant"');
expect(formatAssistantErrorText(msg)).toContain("Message ordering conflict");

View File

@@ -208,6 +208,17 @@ describe("isContextOverflowError", () => {
expect(isContextOverflowError("We're debugging context overflow issues")).toBe(false);
expect(isContextOverflowError("Something is causing context overflow messages")).toBe(false);
});
it("excludes reasoning-required invalid-request errors", () => {
const samples = [
"400 Reasoning is mandatory for this endpoint and cannot be disabled.",
'{"type":"error","error":{"type":"invalid_request_error","message":"Reasoning is mandatory for this endpoint and cannot be disabled."}}',
"This model requires reasoning to be enabled",
];
for (const sample of samples) {
expect(isContextOverflowError(sample)).toBe(false);
}
});
});
describe("error classifiers", () => {
@@ -286,6 +297,17 @@ describe("isLikelyContextOverflowError", () => {
expect(isLikelyContextOverflowError(sample)).toBe(false);
}
});
it("excludes reasoning-required invalid-request errors", () => {
const samples = [
"400 Reasoning is mandatory for this endpoint and cannot be disabled.",
'{"type":"error","error":{"type":"invalid_request_error","message":"Reasoning is mandatory for this endpoint and cannot be disabled."}}',
"This endpoint requires reasoning",
];
for (const sample of samples) {
expect(isLikelyContextOverflowError(sample)).toBe(false);
}
});
});
describe("isTransientHttpError", () => {

View File

@@ -34,6 +34,19 @@ function formatRateLimitOrOverloadedErrorCopy(raw: string): string | undefined {
return undefined;
}
function isReasoningConstraintErrorMessage(raw: string): boolean {
if (!raw) {
return false;
}
const lower = raw.toLowerCase();
return (
lower.includes("reasoning is mandatory") ||
lower.includes("reasoning is required") ||
lower.includes("requires reasoning") ||
(lower.includes("reasoning") && lower.includes("cannot be disabled"))
);
}
export function isContextOverflowError(errorMessage?: string): boolean {
if (!errorMessage) {
return false;
@@ -45,6 +58,10 @@ export function isContextOverflowError(errorMessage?: string): boolean {
return false;
}
if (isReasoningConstraintErrorMessage(errorMessage)) {
return false;
}
const hasRequestSizeExceeds = lower.includes("request size exceeds");
const hasContextWindow =
lower.includes("context window") ||
@@ -85,6 +102,10 @@ export function isLikelyContextOverflowError(errorMessage?: string): boolean {
return false;
}
if (isReasoningConstraintErrorMessage(errorMessage)) {
return false;
}
if (CONTEXT_WINDOW_TOO_SMALL_RE.test(errorMessage)) {
return false;
}
@@ -464,6 +485,13 @@ export function formatAssistantErrorText(
);
}
if (isReasoningConstraintErrorMessage(raw)) {
return (
"Reasoning is required for this model endpoint. " +
"Use /think minimal (or any non-off level) and try again."
);
}
// Catch role ordering errors - including JSON-wrapped and "400" prefix variants
if (
/incorrect role information|roles must alternate|400.*role|"message".*role.*information/i.test(