[BUG] Approving a forwarded teammate permission request with a message silently drops the message
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
When a tmux-backed teammate's permission request is forwarded to the team lead's TUI, the prompt offers the usual accept/reject text inputs ("tell Claude what to do next" on accept, "tell Claude what to do differently" on reject).
Rejecting with a message works. The teammate receives it — its tool result reads Permission for this tool use was denied. … The user said: <message>.
Approving with a message silently discards it. The text is accepted by the prompt, the tool proceeds, and the message reaches nobody: not the teammate, not the lead agent, not the transcript, not any hook. Nothing indicates it was dropped, so the user reasonably believes they have given the teammate an instruction that it never received.
The same asymmetry does not occur for the lead's own, non-forwarded tool calls: there an approval note is attached to the tool result as an extra text block and the model does see it. Only the forwarded (teammate) path loses it.
This looks unintentional rather than by design: the teammate-side machinery to receive an approval note is fully implemented and wired — it is simply never handed the value. Details under Additional Information.
What Should Happen?
An approval message should reach the teammate the way a denial message does — ideally attached to the tool result, which is exactly what already happens for the lead's own calls. Failing that, it should at minimum not be accepted into an input box and then silently discarded.
Steps to Reproduce
- Start a session with an agent team and spawn a tmux-backed teammate.
- Have the teammate make a tool call that cannot be auto-resolved — a
permissions.askrule or a PreToolUse hook returningpermissionDecision: "ask"both work. The teammate pane shows "Waiting for team lead approval" and the request forwards to the lead's TUI. - Deny case (works): choose the reject option, type a message, submit. The teammate's tool result contains
The user said: <message>. - Approve case (bug): repeat, but type a message into the accept input and approve. The tool runs, and the message appears nowhere — neither the teammate's context nor the lead's has any trace of it.
There is no error output to attach: the defining symptom is that the failure is entirely silent.
Is this a regression?
No, this never worked. The lead-side behavior described below is present in 2.1.232, 2.1.233 and 2.1.234; I have not seen it work in any version.
Claude Code Version
2.1.234 (Claude Code)
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Other (kitty, inside tmux)
Additional Information
Root cause (code-level)
Minified identifiers below are from the 2.1.234 bundle and differ between builds; they are included only to point at the exact sites. There are three separate drops along the path, and a fix likely needs all three.
1. The lead never reads the accept feedback when answering a forwarded request.
The prompt does produce it: the dialog result for an approval is {behavior:"allow", updatedInput, feedback} (the option handlers spread ...r.feedback && {feedback: r.feedback}), and the shared prompt hook tracks acceptFeedback and rejectFeedback as separate fields, with hint labels {accept: "tell Claude what to do next", reject: "tell Claude what to do differently"}.
The handler that answers a forwarded request then does:
case "allow": Ahi(agent_id, {decision:"approved", resolvedBy:"leader",
updatedInput: K.updatedInput, permissionUpdates: K.permissionUpdates}, …)
case "deny": Ahi(agent_id, {decision:"rejected", resolvedBy:"leader",
feedback: K.feedback}, …)
K.feedback is read on the deny branch and not on the allow branch. This is where the user's text stops existing.
2. The mailbox wire format has no field that could carry it.
That response is serialised as {request_id, subtype: approved ? "success" : "error", error: <feedback>, updated_input, permission_updates}. error is the only feedback carrier and is only meaningful for subtype: "error". Undelivered messages left in the team inbox files (~/.claude/teams/<team>/inboxes/<agent>.json) show the shape:
{"type":"permission_response","request_id":"perm-…","subtype":"error","error":"Permission denied"}
So even a fixed lead would have nowhere to put the text without a schema addition.
3. The teammate-side dispatcher drops it again — it is called with 2 of 4 arguments.
The teammate registers a callback whose allow handler is declared with four parameters, onAllow(updatedInput, permissionUpdates, feedback, contentBlocks), and correctly forwards the third into handleUserAllow({feedback, contentBlocks, …}), which becomes acceptFeedback, which is appended to the tool result as a {type:"text"} block. That is a complete, working delivery mechanism.
But the mailbox response handler invokes it as:
if (e.decision === "approved") { …; r.onAllow(o, n) } // 2 args — feedback never passed
else { r.onReject(e.feedback) }
So the receiving side is built and ready, and is simply never given the value — on either the wire or the call.
Corroborating asymmetry: hook events
The hook event enumeration includes PermissionDenied with no approved counterpart:
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied, UserPromptSubmit, UserPromptExpansion, SessionStart, SessionEnd, Stop, StopFailure, SubagentStart, SubagentStop, PreCompact, PostCompact, Notification, TeammateIdle, TaskCreated, TaskCompleted, Elicitation, ElicitationResult, ConfigChange, WorktreeCreate, WorktreeRemove, InstructionsLoaded, CwdChanged, FileChanged, DirectoryAdded, MessageDisplay, Setup
The same accept/reject asymmetry therefore shows up at the extension surface too: there is no event on which a user could build a workaround relay. (MessageDisplay carries assistant-message deltas — turn_id/message_id/index/delta — so it is not one either.)
Impact, and why users cannot work around it
Because the text is discarded inside the lead process before it is written to the mailbox, logged, or emitted to any hook, it is not recoverable by any user-side mechanism — no hook, no transcript scrape, no mailbox watcher. The only available workaround is to avoid approve-with-message entirely and deny with the note as the delivery channel, which is a poor substitute: it aborts the tool call the user actually wanted to allow.