Skip to content

Commit 2648932

Browse files
author
1bcMax
committed
fix: don't waste 5 retries on schema errors
When o3 rejected the Task tool schema, users saw '503: 400 Invalid schema for function Task' and the loop retried 5 times, each hitting the same schema error. Total wait: ~30 seconds of pointless retries before surfacing a suggestion. The classifier saw '503' and marked it transient. But schema errors are wrapped in 5xx responses and are NOT transient — they'll fail identically every time. Added a 'schema' error category that's checked BEFORE the generic 5xx branch. Matches on: 'invalid schema', 'array schema missing items', 'invalid tool_use', 'invalid function', 'unsupported parameter', 'invalid request'. Sets isTransient: false and maxRetries: 0 so the error surfaces immediately with the suggestion: 'Tool schema rejected by this model. Try /model to switch to a more permissive model (e.g. sonnet), or upgrade Franklin.' Combined with yesterday's Task schema fix + MCP sanitizer, users now either succeed (99% case) or fail fast with actionable guidance.
1 parent 6d40910 commit 2648932

4 files changed

Lines changed: 42 additions & 4 deletions

File tree

dist/agent/error-classifier.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
* - Auth errors (401) get special handling (token refresh, not retry)
77
* - EPIPE/connection reset handled as network errors (retryable)
88
*/
9-
export type AgentErrorCategory = 'rate_limit' | 'payment' | 'network' | 'timeout' | 'context_limit' | 'overloaded' | 'server' | 'auth' | 'unknown';
9+
export type AgentErrorCategory = 'rate_limit' | 'payment' | 'network' | 'timeout' | 'context_limit' | 'overloaded' | 'server' | 'auth' | 'schema' | 'unknown';
1010
export interface AgentErrorInfo {
1111
category: AgentErrorCategory;
12-
label: 'RateLimit' | 'Payment' | 'Network' | 'Timeout' | 'Context' | 'Overloaded' | 'Server' | 'Auth' | 'Unknown';
12+
label: 'RateLimit' | 'Payment' | 'Network' | 'Timeout' | 'Context' | 'Overloaded' | 'Server' | 'Auth' | 'Schema' | 'Unknown';
1313
isTransient: boolean;
1414
/** Max retries for this error type (overrides default). undefined = use default. */
1515
maxRetries?: number;

dist/agent/error-classifier.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,24 @@ export function classifyAgentError(message) {
103103
suggestion: 'The model is overloaded. Try /model to switch, or wait and /retry.',
104104
};
105105
}
106+
// Schema / tool-definition errors — NOT transient, retrying won't help.
107+
// These can be wrapped in 5xx responses (e.g. '503: 400 Invalid schema'),
108+
// so classify them BEFORE the generic server-error branch below.
109+
if (includesAny(err, [
110+
'invalid schema',
111+
'array schema missing items',
112+
'schema missing',
113+
'invalid tool_use',
114+
'invalid function',
115+
'tool_use_id',
116+
'unsupported parameter',
117+
'invalid request',
118+
])) {
119+
return {
120+
category: 'schema', label: 'Schema', isTransient: false, maxRetries: 0,
121+
suggestion: 'Tool schema rejected by this model. Try /model to switch to a more permissive model (e.g. sonnet), or upgrade Franklin.',
122+
};
123+
}
106124
if (includesAny(err, [
107125
'500',
108126
'502',

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@blockrun/franklin",
3-
"version": "3.6.22",
3+
"version": "3.6.23",
44
"description": "Franklin — The AI agent with a wallet. Spends USDC autonomously to get real work done. Pay per action, no subscriptions.",
55
"type": "module",
66
"exports": {

src/agent/error-classifier.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ export type AgentErrorCategory =
1616
| 'overloaded'
1717
| 'server'
1818
| 'auth'
19+
| 'schema'
1920
| 'unknown';
2021

2122
export interface AgentErrorInfo {
2223
category: AgentErrorCategory;
23-
label: 'RateLimit' | 'Payment' | 'Network' | 'Timeout' | 'Context' | 'Overloaded' | 'Server' | 'Auth' | 'Unknown';
24+
label: 'RateLimit' | 'Payment' | 'Network' | 'Timeout' | 'Context' | 'Overloaded' | 'Server' | 'Auth' | 'Schema' | 'Unknown';
2425
isTransient: boolean;
2526
/** Max retries for this error type (overrides default). undefined = use default. */
2627
maxRetries?: number;
@@ -134,6 +135,25 @@ export function classifyAgentError(message: string): AgentErrorInfo {
134135
};
135136
}
136137

138+
// Schema / tool-definition errors — NOT transient, retrying won't help.
139+
// These can be wrapped in 5xx responses (e.g. '503: 400 Invalid schema'),
140+
// so classify them BEFORE the generic server-error branch below.
141+
if (includesAny(err, [
142+
'invalid schema',
143+
'array schema missing items',
144+
'schema missing',
145+
'invalid tool_use',
146+
'invalid function',
147+
'tool_use_id',
148+
'unsupported parameter',
149+
'invalid request',
150+
])) {
151+
return {
152+
category: 'schema', label: 'Schema', isTransient: false, maxRetries: 0,
153+
suggestion: 'Tool schema rejected by this model. Try /model to switch to a more permissive model (e.g. sonnet), or upgrade Franklin.',
154+
};
155+
}
156+
137157
if (includesAny(err, [
138158
'500',
139159
'502',

0 commit comments

Comments
 (0)