Rebounder Tech Blog

Written by the people who actually run these systems in production.

enabled: false Silently Disabled Claude Code Scheduled Tasks

Published About 6 min readBy the Rebounder engineering team — the people who operate these systems

This article may contain affiliate links. Its content is not affected by advertising.

In short

Claude Code scheduled tasks stop three ways — never starting, dying mid-run, or silently set to enabled: false — and a run that dies mid-run is never retried, so the next slot has to catch up.

Conclusion

Running Claude Code’s scheduled tasks unattended, we found they stop three different ways.

How it stops What happened Fix needed
① Never starts Mac was asleep, or the app was closed None — it runs automatically on the next launch
② Starts, then dies mid-run A dropped connection, so the API was never reached No retry exists. The next slot has to catch up
③ The slot itself gets disabled enabled: false, with no one intending it Check the list daily and re-enable

None of the three show up in the log of the run that actually happened. That’s why we judge by “did the artifact land in git” instead of “did it run.”

What’s actually running

This blog’s (tech.rebounder.jp) articles are written by scheduled tasks in Claude Code’s desktop app. There are 6 slots a day: 4 for writing articles (08:06 / 12:08 / 16:05 / 20:07) and 2 for stocking angles and pulling traffic numbers (11:02 / 17:07). We’ve run this setup for 7 weeks starting 2026-08-16.

Every run happens with no screen, and no human is in the loop. So we can’t assume “someone will notice” when something stops.

① Never starts — this one was fine to leave alone

If the Mac is asleep or the app is closed, nothing happens at that time. But this ran automatically the next time the app launched. In one measurement, the 8/17 20:07 slot completed on 8/18 at 09:59.

This isn’t the one that needs attention.

② Starts, then dies mid-run — there is no retry

This is the one that hurts. The 2026-08-17 20:02 night slot died 4 minutes after starting, with this error:

API Error: Can't reach the API server (ENOTFOUND)

The log was 12 lines, and not a single file had been touched. It died, so it couldn’t record anything itself.

Two days later, on 8/19, the connection dropped for about an hour, and the 11:02 and 12:08 slots died back to back. There was no retry, and that slot was lost for good. Because the run itself dies before reaching the API, writing “retry on failure” into the runbook doesn’t help either — there’s no process left to read it.

Let the next slot catch up

The only lever we had was “the next slot catches up.” Now, at the start of every slot, we count how many articles have gone out today and how many slots have already passed, and write the shortfall in that slot.

We capped the catch-up at 2 articles per run, for three reasons:

  • Publishing several articles on the same topic in one day collides more easily
  • The longer one run takes, the higher the chance it dies mid-run — trying to catch up just creates more of failure mode ②
  • It burns through the stock of material faster

Do the parts you can’t recover first

We also changed the order. Traffic numbers only last 7 days on the Cloudflare side, so a day you miss is gone forever. An article, on the other hand, can always be carried over to the next day. So after the 8/19 miss, we made every article-writing slot pull the traffic numbers first, before touching the article. If even one of the 4 slots runs, that day’s data is captured.

“It ran” and “it did the work” are not the same thing

The 8/18 10:01 stocking slot has an execution record (a last-run timestamp) showing it ran, but it neither stocked angles nor pulled traffic — it just wrote one file and stopped. It had overlapped with a carried-over run (which finished at 10:02).

If you only look at the execution record, this looks like a success.

③ The slot itself gets disabled — missed for a week

This is the one we missed longest. On 2026-09-27–28, 3 of the 4 article-writing slots had been set to enabled: false. We didn’t notice for a week. Daily output dropped from 1–4 articles a day to exactly 1 a day.

The 5 runs right before that had all succeeded — this wasn’t caused by a failure. No one remembered disabling it, either. No one intended to stop it.

Our monitoring dashboard did render this as a “failure.” But it looked identical to a run that died from a dropped connection, so it got lost among 16 other entries. The root problem was that we weren’t distinguishing “one run failed” from “the slot itself is disabled.”

Now, at the start of the first slot each day, we list every scheduled task. If a writing slot is set to false, we flip it back to true and add one line to the report: “X was disabled, so I re-enabled it.” If we ever want a slot intentionally stopped, its name goes into the runbook as an exception. Any slot not named there gets flipped back on every day.

Judge by the artifact, not by self-report

What all three have in common is that trusting the run’s own record misses things. A run that died can’t record anything, and a run that merely executed will call itself a “success.”

So for every slot we decided which file should land in git if that slot actually did its job — an article file for a writing slot, measurement data for a stocking slot — and we judge by whether that file showed up. The morning and evening stocking slots write to the same kind of file, so they wouldn’t be distinguishable from each other; we gave them separate heartbeat files instead.

We measured the wait time instead of guessing it

We’d originally guessed 120 minutes as the time from “run starts” to “artifact appears.” Here’s what we actually measured from 8/17–8/30:

Slot Median Max
08:06 morning article 13 min 113 min
11:02 stocking 107 min 171 min
12:08 midday article 78 min 125 min
16:05 evening article 122 min 163 min
17:07 evening stocking 68 min 130 min
20:07 night article 19 min 169 min

The evening article slot’s median exceeded the window itself. Articles were going out, but more than half were showing up as “failed,” reporting a 33% success rate. We widened the window to 180 minutes and the false alarms went away. A dashboard that cries wolf eventually gets ignored by everyone.

Bonus: an unattended run stuck waiting for permission

Separate from the three ways of stopping, on day one (2026-08-16) we hit a permission check. Since no one can approve anything in an unattended run, the slot just ends the moment a confirmation prompt appears. There were two causes:

  • Chaining multiple commands with && or ; doesn’t match any entry in the allowlist
  • cd <dir> && git ... triggers a confirmation every time, because the target directory’s git hooks could run. This can’t be removed from the allowlist

We avoided both by changing how we write commands: one command per call, specifying the location with git -C <dir> and npm --prefix <dir> instead of cd.

Summary

If you’re going to run Claude Code’s scheduled tasks unattended, assume they will stop. A run that never started catches up on its own, but a run that died mid-run never comes back, and a disabled slot won’t tell anyone. We settled on judging by “did the artifact land,” letting the next slot catch up to 2 articles, and pulling unrecoverable data first in every slot.

Frequently asked questions

Q1What happens to a scheduled task slot while the Mac is asleep?

In our measurements it just ran automatically the next time the app launched — the 20:07 slot once completed the next morning at 09:59. The slot that actually needs attention is one that started but died mid-run; that one is never retried and the slot is lost for the day.

Q2Does a scheduled run retry automatically if it fails partway through?

No. A run that dies from a dropped connection before reaching the API records nothing on its own, and the runbook itself can't add a retry to a run that never came back. So instead, the next slot counts today's shortfall and catches up, capped at 2 articles in one run.

Q3How do you notice that a scheduled task slot has been disabled?

Not from the run log. We check whether the artifact that slot should produce — an article file or measurement data — actually landed in git. We also list every scheduled task at the first slot each day, re-enable any that are set to enabled: false, and note it in the report.

Q4Why does an unattended scheduled run get stuck waiting for permission?

Two causes: chaining commands with && doesn't match any allowlist entry, and cd-ing into a directory before running git triggers a confirmation every time, since the target's git hooks could run. We fixed both by using one command per call, with git -C and npm --prefix instead of cd.

Environment verified

  • Claude Code desktop app scheduled tasks (macOS, local execution)
  • Operated on 6 daily slots (4 article slots + 2 measurement slots) from 2026-08-16 to 2026-10-04

What this article is based on

  • TypeScript file lines 1-32commit 070e64c
  • TypeScript file lines 1-17commit 070e64c
  • TypeScript file lines 39-73commit 070e64c
  • TypeScript file lines 76-96commit 070e64c
  • Markdown file lines 18-56commit 070e64c
  • Markdown file lines 62-90commit 070e64c
  • Markdown file lines 405-423commit 070e64c

Every claim in this article comes from the records above. The repositories we operate are private so we cannot link to them, but which file, which lines, and at which commit we read them is recorded for every article. Nothing here is written from guesswork.