4 Days, 3 Wasted Calls Per Run: My Retry Loop Mistook a Quota-Limit Message for a 'Too Short' Article A developer fixed a retry-loop bug in note2-daily-stock.sh, a shell script that auto-generates product review articles via claude -p, after the script spent three wasted Claude calls per run for four consecutive days (Sept 13–16). The script was misreading the Max plan's one-line quota-limit message as an ordinary "too short" article failure and retrying a condition no rewrite could fix; a grep-based limit-message check now aborts after a single call, deletes partial output, and sends a Discord notification. The same missing detection pattern was found in three sibling scripts: article-daily-stock.sh, maker-daily-stock.sh, and series-daily-stock.sh. Last time I wrote about using a probe to decide which jobs to stop first when the quota runs dry https://zenn.dev/bokuwalily/articles/quota-circuit-priority-probe . This post is about the problem sitting right next to it: my script could tell when the quota was exhausted, but it treated the limit message as if it were real article body text, then kept retrying a failure that no rewrite could ever fix. From September 13 to 16, every run burned 3 claude -p calls to produce nothing. After the fix, it burns 1 and bails out. note2-daily-stock.sh auto-generates product review articles for note2 bokumolily using claude -p . For each product it calls claude -p up to 3 passes to produce the body. Each pass is judged by a quality check article quality ok with criteria like "does the character count exceed MIN CHARS 7,000 ?" and "are there at least 2 affiliate links?" For 4 days, 9/13 through 9/16, this script failed 3 times in a row with the exact same symptom. The cause: when claude -p hits the Max plan's limit, it returns a one-line limit-reached message no TITLE: line , and the script treated that as "an ordinary generation failure where the body is too short." The comment in the actual code records exactly what happened: 2026-09-16: claude -pがクォータ上限メッセージ 1行・TITLE:行なし を返すと 本文が空のまま「文字数不足」として3回とも空振りし、上限中に3回分のclaude呼び出しを浪費していた note2-daily 9/13-9/16 4日連続で同一症状を実測 。上限メッセージは即検出して1回で中断する。 note2-daily-stock.sh:565-567 While the limit is in effect, no amount of rewriting will produce a body. Yet article quality ok only ever said "quality NG reason: not enough characters," so the script concluded "maybe one more pass will fix it" and threw away 2 more pointless claude -p calls during the limit window. Note: A retry loop needs more than the monolithic fact that "it failed." If you don't distinguish "a failure a rewrite can fix" from "a failure a rewrite cannot fix," you'll keep walking into the same wall. The fix is to grep for the limit-message pattern immediately after each pass's generation, and if it matches, abort right there instead of spending the remaining passes. for pass in 1 2 3; do RAW=$ generate article raw "$PRODUCT JSON" "$IMG COUNT" if printf '%s' "$RAW" | grep -qiE "hit your weekly |5-hour |usage |session ?limit|usage limit reached|rate limit|resets? at |in |on ? 0-9 {1,2} : 0-9 {2} ? ap m"; then QUOTA HIT=1 log "ABORT: quota-message検出のため中断 pass $pass $SLUG raw先頭= $ printf '%s' "$RAW" | tr '\n' ' ' | cut -c1-160 " break fi write article file "$RAW" "$OUT" ... done note2-daily-stock.sh:563-572, excerpt When QUOTA HIT is set, the script deletes the half-written output file and any images downloaded along the way, notifies Discord, and exits immediately. Instead of 3 calls, it consumes exactly 1 at that point and retreats. if "$QUOTA HIT" -eq 1 ; then -e "$OUT" && rm -f "$OUT" rm -f "$ROOT/${IMG BASENAME}-p" .jpg discord notify "⚠️ note2-daily: claudeクォータ上限のため中断(3回消費せず中止): $ITEM NAME" exit 1 fi note2-daily-stock.sh:590-595 "Not enough characters" and "limit reached" look alike on the surface the body is nearly empty , but the correct response is completely different. The former might be fixed by a rewrite; the latter won't be fixed no matter how many times you retry until the reset time. Separating those two cases up front with a single grep is the entire fix. There are 3 other scripts that share the same shape as note2-daily-stock.sh — "have claude -p write the body, retry if the quality check fails": article-daily-stock.sh the script that writes this very Zenn article series , maker-daily-stock.sh , and series-daily-stock.sh . I grepped all 3, and none of them contained the limit-message detection pattern. bash $ grep -n "hit your\|usage limit\|QUOTA HIT\|rate limit" \ article-daily-stock.sh maker-daily-stock.sh series-daily-stock.sh 該当なし In particular, run pass in series-daily-stock.sh has almost exactly the same structure that note2 tripped over. php run pass { $1=prompt $2=outfile $3=label local prompt="$1" outfile="$2" label="$3" attempt wait for attempt in 1 2 3; do rm -f "$outfile" timeout 600 "$CLAUDE" ... -p "$prompt" ... "$outfile" ... if -s "$outfile" ; then ... fi log "WARN: pass $label 空出力 attempt $attempt/3 " if "$attempt" -lt 3 ; then wait=$ attempt 30 sleep "$wait" fi done series-daily-stock.sh:254-279, excerpt Waiting 30 seconds, then 60 seconds, on "empty output attempt N/3 " and hammering the same call 3 times for the same reason is essentially the same structure note2 hit on 9/13–9/16. If this script runs during a limit window, it should waste calls in exactly the same way — and it hasn't been fixed yet. maker-daily-stock.sh only has a single generation pass, so it doesn't fail 3 in a row, but the same dates show up in its comments too. 失敗時もキューを1件進める。進めないと同じ題材が先頭に居座り続け、翌日以降も 同じ理由で落ち続ける(2026-09-13〜16 に同一題材が7回連続で失敗した実測)。 maker-daily-stock.sh:412-413 In other words, the Max plan limit on 9/13–9/16 didn't just hit note2 — it dragged the maker pipeline down with it. On the maker side, the underlying problem of "misdiagnosing the limit message as an incomplete body" is still there, exactly as in note2; it's being held together by a different band-aid: "even on failure, at least advance the topic queue." The same root cause has a different emergency patch in each script, applied whenever someone happened to notice. claude -p calls during the limit window maker-daily-stock.sh also failed 7 times in a row on the same topic — the limit hit multiple pipelines simultaneously. But maker's fix is only "advance the queue," and the underlying misdiagnosis remains unfixed there as well as in article-daily-stock.sh and series-daily-stock.sh Next time I plan to write about whether that propagation itself can be automated — a mechanism to mechanically carry a fix found in one script over to its "structurally similar siblings." How do your retry loops tell a fixable failure from one that no retry will ever fix? Written by Lily — I ship iOS apps and automate my content stack with Claude Code. Follow along: Portfolio https://bokuwalily.com · X https://x.com/bokuwalily · GitHub https://github.com/bokuwalily