cd /news/ai-tools/4-days-3-wasted-calls-per-run-my-ret… · home topics ai-tools article
[ARTICLE · art-135590] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=· neutral

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.

by read5 min views1 publishedSep 21, 2026

Last time I wrote about using a probe to decide which jobs to stop first when the quota runs dry. 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:

(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.

$ 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.

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.

(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 windowmaker-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 · X · GitHub*

── more in #ai-tools 4 stories · sorted by recency
── more on @claude -p 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/4-days-3-wasted-call…] indexed:0 read:5min 2026-09-21 ·