[BUG] send_message from unattended/scheduled-task session: worked 2026-07-21, hard-rejected 2026-07-30 (no confirmation path)
Description
mcp__ccd_session_mgmt__send_message, called from an unattended (scheduled-task) session, worked successfully on 2026-07-21 but is rejected outright on 2026-07-30 with the tool's own documented restriction message. Same session category, same routine, same tool — different behavior 9 days apart.
The workflow this tool is relied on for
This is the specific end-to-end mechanism that broke, spelled out because it's the crux of the report:
- A scheduled/automated session detects that a different, unrelated session got cut off mid-task by a usage-limit reset.
- It calls
send_messageto deliver a continuation prompt into that interrupted session — this is documented to land as a new user turn in the target's transcript, queued for when that session is next active. - The user later opens (or the client reconnects to) the target session's own window. That session picks up the queued turn and resumes its interrupted work using its own native context — no rebuilding of that work elsewhere is needed.
On 2026-07-21 this three-step flow worked end to end (see below). On 2026-07-30, step 2 itself is rejected outright, so steps 1 and 3 can never be reached.
Evidence — 2026-07-21 (succeeded)
A user-defined scheduled task (generic purpose: detect and resume work interrupted by a usage-limit reset) fired automatically. The agent called send_message twice against two different target sessions (unrelated ongoing tasks in the same working directory) to hand off continuation instructions after the quota reset. Both calls succeeded — the agent's own summary at the time (paraphrased, session names redacted):
Sent continuation handoffs to both interrupted sessions: notified Session A that quota had refreshed and it should resume its in-progress task; notified Session B likewise with additional findings to fold into its output.
The user then opened those two target sessions directly and confirmed the injected messages caused each session to continue its interrupted work in-place.
Evidence — 2026-07-30 (fails)
Same scheduled task, unchanged since 2026-07-21 (verified: the task's definition/prompt was not edited in the interim), same scenario (automatic quota-refresh trigger, unattended/scheduled-task session type), attempting the identical operation — hand off a continuation message to a different interrupted session (a worklog-drafting session that hit its quota mid-task). Two separate attempts in the same session, both with the same result:
Error calling tool 'mcp__ccd_session_mgmt__send_message': This tool is unavailable in unattended sessions (scheduled-task runs and remote-dispatched trees).
This isn't a permission prompt that could be approved — it's an unconditional rejection with no confirmation path, reproduced twice with different message payloads.
Why this looks like a regression, not documentation catching up to existing behavior
The tool's own description already states: "Unavailable in unattended sessions (scheduled-task runs and remote-dispatched sessions), and cannot deliver to them either." This exact restriction text is also quoted in #82141 (filed 2026-07-29), where the reporter — testing from what appears to be a different session context — got Message queued for session <id>; it will be processed after the in-flight turn finishes if that session stays healthy. rather than an outright rejection.
So across these three data points we have three different observed behaviors for the same documented restriction:
- 2026-07-21, unattended/scheduled-task session → succeeds, message delivered and acted on
- 2026-07-29 (#82141), context unclear → queued, not rejected
- 2026-07-30, unattended/scheduled-task session → hard rejection, no queueing, no confirmation
If the "unavailable in unattended sessions" restriction is intentional and permanent, it should have blocked the 2026-07-21 calls too — it didn't. Something changed in how this restriction is enforced in the intervening period, and it's not reflected in any changelog or PR I could find searching the anthropics/claude-code issue tracker.
Impact
This tool is the only supported mechanism for a scheduled/automated session to hand off continuation context to a different live session after a usage-limit reset (e.g. resuming a session-bound deliverable like a work-log artifact that must be edited by its own originating session). Losing it silently, with no fallback and no confirmation path, breaks an automation workflow that previously worked as designed.
Ask
- Confirm whether this restriction is intended to always apply to scheduled-task/unattended sessions, or whether 2026-07-21's success indicates the restriction used to not be enforced and was tightened since.
- If intentional: update the tool description to also state there is no way to request user confirmation to proceed (currently it reads like a permission gate, not a hard block), and consider surfacing this in the docs gap already flagged in #82141.
- If unintentional: treat as a regression against the 2026-07-21 working behavior.
Environment
- Claude Code desktop app, Windows
- Observed via two runs of the same user-defined scheduled task (a "detect and resume interrupted work on quota reset" routine, name withheld), in the same working directory, task definition unchanged across both runs
3 Comments
Additional data point — 2026-07-30, later same day
Re-tested from a different angle a few hours after the original report was filed. Results add a wrinkle: the restriction is not purely "caller is unattended" — it also fires when the target session is unattended, with a different error string.
Setup: normal/attended session (not scheduled-task, not remote-dispatched), calling
mcp__ccd_session_mgmt__send_messageagainst several different target sessions in the same working directory.scheduledTaskId) → succeeds immediately, no confirmation prompt of any kind, tool resultMessage sent to session <id>.Reproduced twice against two different attended targets.scheduledTaskIdset (i.e. it's the run history of a recurring scheduled task) → rejected, but with wording distinct from the original report:``
`Session <id> is unattended (a scheduled-task run or dispatched session); messages can't be delivered there.
This tool is unavailable in unattended sessions (scheduled-task runs and remote-dispatched trees).This is different from the caller-side message quoted in the original report (
). Same underlying restriction concept (unattended sessions can't participate insend_message`), but two different error strings depending on whether the caller or the target is the unattended party.Separately, re-reading the transcript of the scheduled-task session that filed this report: it retried the caller-side call three times within its own run, all three got the exact caller-side rejection text from the original report, no queueing, no confirmation path offered on any attempt — consistent with this still being a hard, unconditional block as of today rather than a flaky/intermittent one.
Net: attended-caller → attended-target still works cleanly and instantly (no regression there). The two "unattended" rejection paths (unattended caller; unattended target) both still fully block delivery, each with their own message text, neither offering a confirmation route. Filing this as a comment rather than a new issue since it's the same restriction, just triangulated from both sides.
Thanks for the unusually detailed write-up — the two-date comparison made this much easier to reason about.
I couldn't reproduce this directly:
send_messagehere comes from the desktop app's session-management tooling, not the CLI. On Linux with the released 2.1.233 build that tool isn't present at all, so there was nothing to exercise — marking this as untestable on my end rather than not-reproduced.On intent: the restriction is part of the tool's own documented behavior, and that wording was already in place before both of your runs. So the 2026-07-30 rejection looks like the intended behavior, and the 2026-07-21 success looks like the restriction simply not being enforced yet. One clarification on your third data point — the "message queued" result in the other issue came from an ordinary interactive session, where queueing is the documented outcome, so that isn't a third behavior.
That said, your expectation is reasonable and the current wording earns the confusion: it reads like a permission gate when it's an unconditional block, and there's no documented alternative for an automated run to hand work back to a live session. We'll look at rewording it and covering the gap in the docs.
If you can share the desktop app version for each of the two runs, that would help pin down exactly when enforcement changed.
🤖 Generated with Claude Code
Desktop app version for each run — partial answer
Dug into this from the client machine's side (Windows, single device, no other install history to cross-reference).
2026-07-30 (rejected run): confirmed
1.24012.9.0Found a hard record in the Windows AppX deployment event log (
Microsoft-Windows-AppXDeploymentServer/Operational): an update event at 2026-07-25 01:03 (local) showsClaude_1.24012.1.0_x64 is updating to Claude_1.24012.9.0_x64. The next update after that didn't happen until 2026-08-04 (→1.24012.11). So on 2026-07-30 the installed version was unambiguously1.24012.9.0— no update occurred in that window.2026-07-21 (succeeded run): can't confirm
That same event log is a fixed-size (5MB) ring buffer, currently holding ~4,700 records, and its oldest surviving entry is 2026-07-23 15:44 — the 2026-07-21 window has already rotated out and there's no other local source (single device, no backup log) to recover it. The version immediately preceding the 07-25 update was
1.24012.1.0, but I can't confirm whether that was already active on 07-21, or whether an earlier update happened between 07-21 and 07-25 that also got rotated out before I could check. So this one is an educated guess at best (1.24012.1.0or earlier in that line), not a confirmed data point.One more wrinkle — not sure which version number you actually need
There appear to be two independently-versioned things bundled in the desktop app:
1.24012.x/1.26832.xscheme above — this is what I could get hard evidence for).2.1.233you mentioned, which also shows up asrequired_versionstrings in the app's ownmain.logand matches the@anthropic-ai/claude-codenpm package's release cadence.If it's the second one you need for pinning down enforcement timing, let me know and I'll see what I can reconstruct from the npm publish timeline (public, not machine-specific, so weaker evidence) for both dates.
🤖 Generated with Claude Code