Description
The ask_followup_question tool intermittently fails with the error
missing required parameter follow_up, even though the model did supply a
follow_up value. The failure occurs when a model emits follow_up as a single
bare object (one suggestion) instead of wrapping it in an array:
// What the model sometimes emits (rejected):
{ "question": "Proceed?", "follow_up": { "text": "Yes", "mode": null } }
// What the tool requires (accepted):
{ "question": "Proceed?", "follow_up": [{ "text": "Yes", "mode": null }] }
The tool's JSON schema (src/core/prompts/tools/native-tools/ask_followup_question.ts)
declares follow_up as type: "array" with minItems: 1 -- so a single
suggestion is valid. The problem is the missing array wrapper, not the number
of suggestions. The tool description text ("a list of 2-4 suggested answers")
likely nudges some models into thinking a single answer does not need an array.
Root Cause
There are two parser surfaces, but only one is responsible for the runtime error:
NativeToolCallParser.parseToolCall (line ~825) -- the finalized tool call
path that produces the nativeArgs actually passed to tool execution. It sets
follow_up: args.follow_up with no coercion, so a bare object passes through
unchanged.
AskFollowupQuestionTool.execute (line ~38) -- guards with
!follow_up || !Array.isArray(follow_up) and, on failure, calls
recordMissingParamError("follow_up"). Because the value is a non-array object,
this guard fires and produces the misleading "missing parameter" message even
though the parameter was present.
The streaming/partial path
(NativeToolCallParser.createPartialToolUse, line ~480) also drops a non-array
follow_up to undefined, but its output is only consumed by
AskFollowupQuestionTool.handlePartial, which reads question only and never
touches follow_up. It is therefore not a cause of the observed error, only
a cosmetic inconsistency in partial UI rendering.
Affected Files
src/core/assistant-message/NativeToolCallParser.ts (lines ~480, ~825)
src/core/tools/AskFollowupQuestionTool.ts (lines ~38, ~46)
Proposed Fix
- In
NativeToolCallParser.parseToolCall, coerce a bare-object follow_up into a
single-element array before assigning nativeArgs (check Array.isArray first
so an existing array is never re-wrapped).
- Apply the same coercion in
createPartialToolUse for partial-render consistency.
- Correct the misleading error: when
follow_up is present but not an array,
report a malformed-parameter error rather than "missing required parameter".
- (Optional robustness) Filter out suggestions lacking a
text field so a
malformed single suggestion fails clearly rather than producing an empty answer.
Test Procedure
- Add a unit test for
NativeToolCallParser.parseToolCall asserting that a bare
object follow_up is coerced to a single-element array in nativeArgs.
- Add a unit test for
AskFollowupQuestionTool.execute asserting that a single
valid suggestion (array of one) succeeds and reaches task.ask.
- Verify that an existing multi-suggestion array still works unchanged.
- Manual: trigger a follow-up question in F5 development mode and confirm a
single-suggestion response renders and is accepted.
Description
The
ask_followup_questiontool intermittently fails with the errormissing required parameter follow_up, even though the model did supply afollow_upvalue. The failure occurs when a model emitsfollow_upas a singlebare object (one suggestion) instead of wrapping it in an array:
The tool's JSON schema (
src/core/prompts/tools/native-tools/ask_followup_question.ts)declares
follow_upastype: "array"withminItems: 1-- so a singlesuggestion is valid. The problem is the missing array wrapper, not the number
of suggestions. The tool description text ("a list of 2-4 suggested answers")
likely nudges some models into thinking a single answer does not need an array.
Root Cause
There are two parser surfaces, but only one is responsible for the runtime error:
NativeToolCallParser.parseToolCall(line ~825) -- the finalized tool callpath that produces the
nativeArgsactually passed to tool execution. It setsfollow_up: args.follow_upwith no coercion, so a bare object passes throughunchanged.
AskFollowupQuestionTool.execute(line ~38) -- guards with!follow_up || !Array.isArray(follow_up)and, on failure, callsrecordMissingParamError("follow_up"). Because the value is a non-array object,this guard fires and produces the misleading "missing parameter" message even
though the parameter was present.
The streaming/partial path
(
NativeToolCallParser.createPartialToolUse, line ~480) also drops a non-arrayfollow_uptoundefined, but its output is only consumed byAskFollowupQuestionTool.handlePartial, which readsquestiononly and nevertouches
follow_up. It is therefore not a cause of the observed error, onlya cosmetic inconsistency in partial UI rendering.
Affected Files
src/core/assistant-message/NativeToolCallParser.ts(lines ~480, ~825)src/core/tools/AskFollowupQuestionTool.ts(lines ~38, ~46)Proposed Fix
NativeToolCallParser.parseToolCall, coerce a bare-objectfollow_upinto asingle-element array before assigning
nativeArgs(checkArray.isArrayfirstso an existing array is never re-wrapped).
createPartialToolUsefor partial-render consistency.follow_upis present but not an array,report a malformed-parameter error rather than "missing required parameter".
textfield so amalformed single suggestion fails clearly rather than producing an empty answer.
Test Procedure
NativeToolCallParser.parseToolCallasserting that a bareobject
follow_upis coerced to a single-element array innativeArgs.AskFollowupQuestionTool.executeasserting that a singlevalid suggestion (array of one) succeeds and reaches
task.ask.single-suggestion response renders and is accepted.