Scheduled task session hangs forever on unattended permission prompt, then per_task_limit silently starves all future runs
Status Open
Maintainer reply None cached
Workaround ✓ Mentioned in description ↑
Activity 0 comments · opened Aug 15, 2026
Summary
In the macOS desktop app, a scheduled task session that hits a tool permission prompt while running unattended hangs forever (no timeout, no failure state). Worse, the hung session still counts against the task's dispatch limit, so the scheduler then logs per_task_limit (active=1, limit=1) and silently skips every subsequent scheduled run of that task until a human interacts with the app and un-freezes the zombie session. One unanswered prompt at 2:49 AM turned into three consecutive nights of a broken nightly job, in two different-looking failure modes.
Environment
- Claude desktop app on macOS (Darwin 25.6.0, Apple Silicon)
- Scheduled tasks feature (
CCDScheduledTasks), recurring cron task running Bash/Edit tools against a local Obsidian vault - Machine never sleeps (
pmset:sleep 0), app running continuously
Repro / what happened
- A recurring scheduled task (cron
47 2 * * *) starts at ~2:49 AM and, ~40s in, issues a Bash pipeline not covered by any allowlist (grep ... | awk ... | sort | uniq -c | sort -rn). - The harness emits a tool permission request. Nobody is at the machine (it's 2:49 AM — the entire point of a scheduled task).
- The session freezes mid-tool-call indefinitely. The transcript JSONL just stops after the
tool_useentry. Hours later, on the next user interaction with the app, the pending request aborts:Tool permission request failed: AbortError: Tool permission stream closed before response received(night 1), or the prompt gets answered by the user and the session resumes and keeps executing its 28-hour-old instructions (night 2 → collided with the recovery jobs that had since run). - Meanwhile the frozen session still counts as active, so the next night's scheduled run never dispatches.
main.logshows, every minute for hours:
[CCDScheduledTasks] Skipping dispatch for nightly-task-triage: per_task_limit (active=1, limit=1)
- When user interaction finally released the zombie at ~7:26 AM, the scheduler immediately fired the missed run:
[CCDScheduledTasks] Spawning new session for scheduled task nightly-task-triage { cronExpression: '47 2 * * *', fireAt: undefined, lastRunAt: '2026-08-15T11:26:44.440Z', missed: '2026-08-15T06:47:00.000Z' }
Related log lines from the auto-approval layer:
[CCDScheduledTasks] Not auto-approving "Bash" in scheduled task "triage-watchdog": rule(s) not in stored approvals: Bash (stored count=1)
[CCDScheduledTasks] Not auto-approving "Bash" in scheduled task "nightly-task-triage": no suggestions on request
Expected behavior
Some or all of:
- A permission request raised by a scheduled (unattended) session should time out after a bounded period and fail the tool call (letting the session handle the denial or end), rather than suspending the session forever.
- A session frozen on a permission prompt should not count as "active" against
per_task_limitindefinitely — or at minimum the skip should surface as a visible failure (notification / task status) instead of a silent minute-by-minute log line. - A resumed session whose pending prompt is finally answered hours later probably should not silently continue a long-stale scheduled run (it collided with the recovery jobs that had run in the meantime).
Workarounds we applied (in case useful to others)
- Project-level
permissions.allowrules in.claude/settings.jsoncovering every read-only command the scheduled task uses, so it never prompts. - A lock-directory protocol inside the task prompts (atomic
mkdir+ owner token + staleness takeover) so late-resumed zombie runs stand down instead of double-writing.