Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
YouTube Security VideosAnonymous Official: I'm begging you to understand this..(20.09.2026 um 21:30 Uhr)
Sichere ProgrammierungHow to Monitor Cron Jobs with a Simple HTTP Health Check(20.09.2026 um 23:14 Uhr)
Sichere ProgrammierungWhy my builds don't run on my laptop(20.09.2026 um 23:15 Uhr)
Sichere ProgrammierungDesigning offline-first when there's no server(20.09.2026 um 23:16 Uhr)
YouTube Security VideosAnonymous Official: I'm begging you to understand this..(20.09.2026 um 21:30 Uhr)
Sichere ProgrammierungHow to Monitor Cron Jobs with a Simple HTTP Health Check(20.09.2026 um 23:14 Uhr)
Sichere ProgrammierungWhy my builds don't run on my laptop(20.09.2026 um 23:15 Uhr)
Sichere ProgrammierungDesigning offline-first when there's no server(20.09.2026 um 23:16 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

How I Auto-Resume a Rate-Limited Claude Code Session with PROGRESS.md and Retries

Reagiere als Erste:r — dein Feedback zählt!

My previous post, Making costs visible by monitoring output tokens, was about stopping overuse. This time it's the opposite problem: how to keep waking up a session that has stalled — the story of resume-on-ratelimit.sh.

I got burned more than once by handing a long task to Claude Code, stepping away from my desk, and coming back to find it had halted on a rate limit. Restarting by hand is tedious, and when the restart is delayed, it forgets the earlier context and starts over. This article is a record of how I solved that problem in 20 lines of shell script.

The problem: long tasks stall halfway on rate limits

When you run a headless task with claude -p, the process exits non-zero the moment it hits your plan's 5h block or 7d block. Two things go wrong here.

  1. The record of "how far the task got" disappears — a plain re-run starts over from the beginning
  2. You don't notice — if you're not watching the terminal, you don't even know it stopped

The pattern I settled on: use PROGRESS.md as a "handoff ticket" and carry the work forward with --continue retries.

Using PROGRESS.md as a handoff ticket

When I hand a task to Claude, I include an instruction in the first prompt: "update PROGRESS.md at each work boundary." Claude naturally follows this, continuously writing completed steps, remaining steps, and notes into the file.

# PROGRESS.md(Claude が自動更新)

## 完了
- [x] 依存パッケージの調査
- [x] ディレクトリ構造の設計

## 進行中
- [ ] 各モジュールのスケルトン生成

## 次に実行すること
モジュールAのテストを先に書く。設計メモは ./design.md 参照。

With this in place, the restart prompt can be a single line: "Read PROGRESS.md and continue the interrupted work". Claude reads the file and resumes from what's left.

The full resume-on-ratelimit.sh

Here is the complete script quoted from the real file (~/.claude/scripts/resume-on-ratelimit.sh).

#!/usr/bin/env bash
# レートリミットで止まったら自動で再開するラッパー
# 使い方: bash resume-on-ratelimit.sh [追加の指示]
#         bash resume-on-ratelimit.sh "PROGRESS.mdを読んで作業を再開して"

set -euo pipefail

WAIT_MINUTES=${WAIT_MINUTES:-5}
MAX_RETRIES=${MAX_RETRIES:-20}
TASK="${1:-PROGRESS.mdを読んで中断した作業を続けて。作業済みなら何もしない。}"
RETRY=0

notify() {
  osascript -e "display notification \"$1\" with title \"Claude Code\"" 2>/dev/null || true
}

echo "[$(date '+%H:%M')] 起動: $TASK"

while [ $RETRY -lt $MAX_RETRIES ]; do
  if [ $RETRY -eq 0 ]; then
    claude --dangerously-skip-permissions --continue -p "$TASK"
    EXIT=$?
  else
    echo "[$(date '+%H:%M')] リトライ $RETRY / $MAX_RETRIES"
    claude --dangerously-skip-permissions --continue -p "PROGRESS.mdを読んで中断した作業を続けて。"
    EXIT=$?
  fi

  if [ $EXIT -eq 0 ]; then
    echo "[$(date '+%H:%M')] 完了"
    notify "Claude Code: 作業完了"
    exit 0
  fi

  RETRY=$((RETRY + 1))
  echo "[$(date '+%H:%M')] レートリミット検出 (exit: $EXIT)。${WAIT_MINUTES}分後にリトライ..."
  notify "Claude Code: レートリミット。${WAIT_MINUTES}分後に再開します"
  sleep $((WAIT_MINUTES * 60))
done

echo "最大リトライ回数に達しました"
notify "Claude Code: 最大リトライ超過。手動確認してください"
exit 1

The whole design fits in a 20-line loop.

The exit code is the only signal for detecting a rate limit

The claude command returns exit 0 on a clean finish and non-zero otherwise. As it stands, any exit code other than 0 on a rate limit can be used for the check. I do not strictly separate out "this is the rate-limit-specific code."

The script's premise is a deliberate simplification: non-zero = a state that should wait and retry. It has the side effect of also retrying tasks that are completely failing, but capping the count with MAX_RETRIES prevents an infinite loop.

--dangerously-skip-permissions is essential for unattended operation. Without it, a confirmation prompt appears on every tool use and blocks. That said, when you use this flag, narrow allowedTools in the prompt or scope it strictly to trusted, task-specific work.

The MAX_RETRIES guard

With the defaults MAX_RETRIES=20 and WAIT_MINUTES=5, it waits for at most 100 minutes (20 × 5 min). A 7d block resets a week later, so in practice these parameters cover "rate limits within a few hours."

They can be overridden via environment variables, so you can tune them to the situation.

# 10分ごとに最大5回だけ待つ例
WAIT_MINUTES=10 MAX_RETRIES=5 bash resume-on-ratelimit.sh "重いタスク"

When MAX_RETRIES is reached, it exits with exit 1 and fires a notification. A parent script or launchd can catch this exit 1 and wire it up to an alert.

Combining with macOS notifications

notify() fires a macOS system notification via osascript.

notify() {
  osascript -e "display notification \"$1\" with title \"Claude Code\"" 2>/dev/null || true
}

The || true is there so that even under the script's set -e, a failed notification (no notification permission, etc.) doesn't kill the process.

Notifications fire at three moments: when a rate limit is detected, when a retry succeeds, and when MAX_RETRIES is exceeded. Even when you're away from your desk, the Dock bounce lets you notice.

Integration with autopilot.sh: three launches via launchd

autopilot.sh is launched by launchd (com.lily.autopilot.plist) three times a day: at 2:00, 5:00, and 23:00.

<key>StartCalendarInterval</key>
<array>
  <dict><key>Hour</key><integer>2</integer><key>Minute</key><integer>0</integer></dict>
  <dict><key>Hour</key><integer>5</integer><key>Minute</key><integer>0</integer></dict>
  <dict><key>Hour</key><integer>23</integer><key>Minute</key><integer>0</integer></dict>
</array>

2 AM and 11 PM are the "set a long task running" hours; 5 AM is meant to "make progress before I wake up." What covers the gaps outside these launch windows is resume-on-ratelimit.sh, which I use as a wrapper when I manually run a long task.

autopilot.sh is designed to stop autonomously after one Phase (up to 40 turns), so it doesn't need a retry loop, but resume-on-ratelimit.sh is for the case where the user wants to "keep it running until this process finishes."

Pitfalls I hit

  • Re-running without --continue starts a new session → the previous context is gone, and even reading PROGRESS.md, it has no memory of "which files it was touching." Always use --continue together with it.
  • If you forget to instruct it to update PROGRESS.md, the handoff ticket comes up empty → the restart prompt whiffs on "do nothing if already done" and just ends. Always put "update PROGRESS.md at each boundary" in the first prompt.
  • A non-zero exit isn't always a rate limit → if it dies on a syntax error or misconfiguration, it will keep retrying. Once you exceed MAX_RETRIES, manual verification is needed.
  • Watch out for combining set -euo pipefail with || true → if another command sneaks in before you capture EXIT=$?, the exit code gets overwritten. If you modify the script, be careful about when you capture the variable.
  • launchd jobs are skipped while macOS is asleepresume-on-ratelimit.sh, being a shell sleep loop, resumes after waking even if it was asleep. If you need the autopilot side, configure wake on schedule with pmset.

Summary

  • Use PROGRESS.md as a handoff ticket: just instruct Claude to "update it at each boundary," and the restart prompt is a single line
  • Exit-code checks are enough for rate-limit detection: non-zero → wait → resume with --continue, a loop you can build in 20 lines
  • A MAX_RETRIES guard is essential: set an upper bound so a fully stuck task isn't retried forever
  • macOS notifications are a one-liner with osascript: don't forget || true, so a failed notification doesn't kill the process under set -e
  • Its role differs from the launchd autopilot: autopilot is a scheduled, autonomous improvement loop; resume-on-ratelimit is an attended-job wrapper for "just until this task finishes"

Next time I'll write about a problem that ironically emerged because this automation foundation grew — the story of how skills and agents multiplied until context injection hit 228 KB, and the audit that trimmed it to 48 KB.

Written by **Lily* — I ship iOS apps and automate my content stack with Claude Code.
Follow along: Portfolio · X · GitHub*

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten How I Auto-Resume a Rate-Limited Claude Code Session with PROGRESS.md and Retries

Thematisch verwandte Begriffe: AutoResume, RateLimited, Claude, Code · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-93957 | A vulnerability has been found in olivier-ls PHP-FTS up to 1.1.3. This a…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick