ScheduleWakeup reports a scheduled time outside /loop, then never fires

Status Open
Maintainer reply None cached
Activity 1 comment · opened Jul 30, 2026

Bug: ScheduleWakeup reports success outside /loop, then never fires

Component: Claude Code — ScheduleWakeup tool
Severity: Medium–High for unattended/long-running work
Reproducibility: Consistent (observed repeatedly)

Summary

ScheduleWakeup accepts a call made outside an active /loop session, returns a success message
naming a concrete wake time
, and then never fires. The success string also tells the model to stop
working for the turn, so the model ends its turn waiting on a wakeup that structurally cannot occur.

Repro

  1. Start a normal Claude Code session (no /loop).
  2. Have the model call ScheduleWakeup with a plain prompt (not a /loop input, not the

<<autonomous-loop-dynamic>> sentinel), e.g. delaySeconds: 1200.

  1. Observe the tool result.
  2. Wait past the stated time.

Expected

One of:

  • the call is refused with a clear reason ("no active /loop; ScheduleWakeup only resumes a

dynamic loop"), or

  • it works as a general deferred re-invocation.

Either is fine. What matters is that the return value reflects reality.

Actual

The tool returns, verbatim:

Next wakeup scheduled for 10:31:00 (in 1221s). Nothing more to do this turn — the harness
re-invokes you when the wakeup fires or a task-notification arrives.

No wakeup occurs. The session sits idle until the user speaks. Confirmed by the transcript: there is no
model turn between the ScheduleWakeup call and the user's next message, so no re-invocation happened.

Why this is worse than a plain no-op

The failure is actively misleading in three ways:

  1. It states a specific time. 10:31:00 reads as a scheduling confirmation, not a best effort.
  2. It instructs the model to stand down — "Nothing more to do this turn". A correct-behaving model

should stop after a successful schedule. So the message converts a silent no-op into a deliberate,
justified stall.

  1. There is no observable signal of failure. Nothing is logged, no later error surfaces. The only way

to detect it is to notice that time passed and nothing happened — which requires a human.

Impact

In an unattended or long-running session this is a silent indefinite stall. Concrete instance:

  • A background workflow was running and produced no journal file, so a completion notification was the

only passive signal — and that notification had already failed to arrive once (a prior run of the same
workflow hung for ~8 hours without notifying).

  • Precisely because passive signals had proven unreliable, the model scheduled an active check.
  • The wakeup never fired, so the workflow went unmonitored for 23 minutes until the user intervened.

The pattern is self-defeating: the tool is most attractive exactly when passive notification is untrusted,
which is exactly when its silent failure is most costly.

Suggested fixes (in preference order)

  1. Refuse the call outside an active dynamic /loop, with a reason. Cheapest, and it makes the

documented constraint enforceable rather than advisory.

  1. If refusal is undesirable, change the return string so it cannot be read as a confirmation —

e.g. No active /loop; nothing scheduled. Do not end your turn expecting a wakeup. The current
wording is the harmful part.

  1. Longer term, consider a general deferred re-invocation primitive. The underlying need — "check

back on background work in N minutes" — is real and currently has no reliable mechanism when a task
emits no notification.

Notes

  • The tool's own description does scope it to /loop dynamic mode, so a model calling it outside that

context is misusing it. The bug is not that it declines to work — it is that it claims to have
worked.
A tool documented as loop-only should not return a scheduled timestamp when no loop exists.

  • Related gap that made this matter: a background Workflow that hangs produces no completion

notification and no result file, and is therefore indistinguishable from one still running. The
only reliable liveness signal found was working-tree file mtimes.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗