Link to a minimal reproduction
Any Linear-channel agent with one approval-gated tool reproduces it deterministically (steps below) — the trigger is the channel's per-prompt context, not app code.
Steps to reproduce
- Agent with an authored tool gated by
approval: always(), an Anthropic model (direct @ai-sdk/anthropic or an Anthropic-compatible endpoint), and the built-in Linear channel (eve/channels/linear).
- From Linear, delegate an issue so the model calls the gated tool → the elicitation "Approve tool call: / Yes / No" is posted, turn parks.
- Reply "Yes" in the Agent Session.
- Next model call 400s:
AI_APICallError: messages.N: `tool_use` ids were found without `tool_result` blocks immediately after: toolu_… Each `tool_use` block must have a corresponding `tool_result` block in the next message.
- Every subsequent prompt rebuilds the same corrupted history — the session is permanently dead.
Current vs. expected behavior
Root cause (traced through dist/src/harness/tool-loop.js, dist/src/harness/input-requests.js, ai@7.0.14, @ai-sdk/anthropic@4.0.7):
- The Linear channel sends
context (the <linear_context> block + previousComments) on every prompt, including the one that answers an approval (dispatchAgentSession → send({ context: [formatLinearContextBlock(t), …] })).
resolvePendingInput correctly resolves "Yes" and builds [...history, assistant(tool_use + tool-approval-request), tool(tool-approval-response)].
executeStepBody then appends S.input.context as user messages after that tool message, so the prompt no longer ends with the approval response.
- The AI SDK's
collectToolApprovals only inspects messages.at(-1). It now sees a user message → no approvals collected → the approved tool never executes and no tool_result is produced.
- Client-side validation doesn't catch it (
convertToLanguageModelPrompt excuses dangling tool_use ids that have an approval response anywhere in history), and @ai-sdk/anthropic silently continues over tool-approval-response parts — so Anthropic receives a dangling tool_use and rejects the request.
Expected: approving a gated tool call resumes the run — the tool executes and its tool_result lands adjacent to the tool_use, regardless of whether the channel attached context to the approving prompt.
Note the asymmetry: resolvePendingInput already defers the user's own text (queueDeferredStepInput) while an approval batch resolves, but context is exempt from that deferral and gets appended in the same step. Possible fixes:
- defer
context the same way message is deferred when the resolved batch contains approval requests; or
- insert context user messages before the pending batch's
responseMessages instead of after the tool message; or
- don't rely on the AI SDK's last-message scan: have the harness collect/execute approvals from the resolved batch explicitly.
This is channel-agnostic in principle (any channel passing context on the approval-answering prompt breaks), but the Linear channel always does, so every Linear approval is affected. Telegram/eve-TUI approvals usually work, which is probably why this survived testing.
Likely related (same invariant, different triggers): #460, #236, #203.
Workaround we're running in production — a pnpm patch on dist/src/harness/tool-loop.js that skips the context push when the resolved messages end in a tool message carrying a tool-approval-response (the dropped block is a duplicate; the same context is already in history from the turn that parked the approval):
let approvalTail=(()=>{let e=j[j.length-1];return e?.role===`tool`&&Array.isArray(e.content)&&e.content.some(e=>e?.type===`tool-approval-response`)})();
if(S.input?.context!==void 0&&!approvalTail)for(let e of S.input.context)j.push({content:e,role:`user`});
eve version
0.19.0 (also reproduced on 0.16.2; the context-push code is unchanged between them)
Environment
Node 24.x (Vercel), pnpm 9.15.0, ai@7.0.14, @ai-sdk/anthropic@4.0.7
Where does the bug occur?
Production (eve start / deployed on Vercel)
Deployment
Vercel, Linear channel via eve/channels/linear with default events
Build and runtime logs
[eve:harness.tool-loop] tool-loop stream error {
error: {
message: 'AI_APICallError: messages.4: `tool_use` ids were found without `tool_result` blocks immediately after: toolu_01LUgzdHzc49TgFQSaGGunTe. …',
name: 'AI_APICallError',
detail: 'APICallError … at _AnthropicLanguageModel.doStream … at async run (file:///var/task/_libs/eve.mjs:40164:11)'
}
}
[eve:harness.tool-loop] model call failed terminally { sessionId: 'wrun_01KWM9VACG43PCYFVJKXNBG0T9', turnId: 'turn_1', … }
Additional context
The failure is unrecoverable by design of the resume path: the pending batch is cleared on the first resolved-but-400 attempt, and the corrupted history is durable, so the user sees "This session could not recover from an error … Start a new Linear agent session".
Link to a minimal reproduction
Any Linear-channel agent with one approval-gated tool reproduces it deterministically (steps below) — the trigger is the channel's per-prompt context, not app code.
Steps to reproduce
approval: always(), an Anthropic model (direct@ai-sdk/anthropicor an Anthropic-compatible endpoint), and the built-in Linear channel (eve/channels/linear).Current vs. expected behavior
Root cause (traced through
dist/src/harness/tool-loop.js,dist/src/harness/input-requests.js,ai@7.0.14,@ai-sdk/anthropic@4.0.7):context(the<linear_context>block +previousComments) on every prompt, including the one that answers an approval (dispatchAgentSession→send({ context: [formatLinearContextBlock(t), …] })).resolvePendingInputcorrectly resolves "Yes" and builds[...history, assistant(tool_use + tool-approval-request), tool(tool-approval-response)].executeStepBodythen appendsS.input.contextasusermessages after that tool message, so the prompt no longer ends with the approval response.collectToolApprovalsonly inspectsmessages.at(-1). It now sees ausermessage → no approvals collected → the approved tool never executes and notool_resultis produced.convertToLanguageModelPromptexcuses danglingtool_useids that have an approval response anywhere in history), and@ai-sdk/anthropicsilentlycontinues overtool-approval-responseparts — so Anthropic receives a danglingtool_useand rejects the request.Expected: approving a gated tool call resumes the run — the tool executes and its
tool_resultlands adjacent to thetool_use, regardless of whether the channel attached context to the approving prompt.Note the asymmetry:
resolvePendingInputalready defers the user's own text (queueDeferredStepInput) while an approval batch resolves, butcontextis exempt from that deferral and gets appended in the same step. Possible fixes:contextthe same waymessageis deferred when the resolved batch contains approval requests; orresponseMessagesinstead of after the tool message; orThis is channel-agnostic in principle (any channel passing
contexton the approval-answering prompt breaks), but the Linear channel always does, so every Linear approval is affected. Telegram/eve-TUI approvals usually work, which is probably why this survived testing.Likely related (same invariant, different triggers): #460, #236, #203.
Workaround we're running in production — a
pnpm patchondist/src/harness/tool-loop.jsthat skips the context push when the resolved messages end in a tool message carrying atool-approval-response(the dropped block is a duplicate; the same context is already in history from the turn that parked the approval):eve version
0.19.0 (also reproduced on 0.16.2; the context-push code is unchanged between them)
Environment
Node 24.x (Vercel), pnpm 9.15.0, ai@7.0.14, @ai-sdk/anthropic@4.0.7
Where does the bug occur?
Production (
eve start/ deployed on Vercel)Deployment
Vercel, Linear channel via
eve/channels/linearwith default eventsBuild and runtime logs
Additional context
The failure is unrecoverable by design of the resume path: the pending batch is cleared on the first resolved-but-400 attempt, and the corrupted history is durable, so the user sees "This session could not recover from an error … Start a new Linear agent session".