Skip to content

Linear channel: approving a HITL tool call permanently kills the session — channel context is appended after the approval response, so collectToolApprovals never sees it (dangling tool_use → Anthropic 400) #529

Description

@Lukavyi

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

  1. 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).
  2. From Linear, delegate an issue so the model calls the gated tool → the elicitation "Approve tool call: / Yes / No" is posted, turn parks.
  3. Reply "Yes" in the Agent Session.
  4. 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.
    
  5. 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):

  1. The Linear channel sends context (the <linear_context> block + previousComments) on every prompt, including the one that answers an approval (dispatchAgentSessionsend({ context: [formatLinearContextBlock(t), …] })).
  2. resolvePendingInput correctly resolves "Yes" and builds [...history, assistant(tool_use + tool-approval-request), tool(tool-approval-response)].
  3. executeStepBody then appends S.input.context as user messages after that tool message, so the prompt no longer ends with the approval response.
  4. 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.
  5. 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".

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingp1tools

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions