æå120äžã®èªååç°å¢ããããæ5æ¬åæã«æ¢ãŸã£ãŠããŸããããã°ãéãããå šéšã«åãè¡ã䞊ãã§ãããã倱æããã®ã§ãã1åå®è¡ããŸãããããã1åã倱æããã1åã倱æãæåã®å€±æãã63åãçµã£ãŠãæ®ã£ãŠããã®ã¯æåã®å€±æãšãŸã£ããåãçµæã§ããã
ãªããã®ä»çµã¿ãå¹ãã®ã
ã¹ã¯ãªãããã¯ãããŠæžãããšãããªãã©ã€ã¯ãããã圢ãããŠãããšæã£ãŠããŸããã
for attempt in range(3):
try:
result = do_something()
break
except Exception as e:
time.sleep(2)
ããã§åã£ãŠããŸãããããã¯ãŒã¯ãäžç¬åããããµãŒããŒãäžæçã«éããªã£ãããããã倱æãªã確ãã«ããã§çŽããåé¡ã¯ã次ã®è©Šè¡ãæåããã¯ãã ããšããåæãããã¹ãŠã®å€±æã«åããåœãŠã¯ããŠãããšããã§ãã
皌åããŠãããžã§ããå¢ããŠãããšããã®åæã厩ãã倱æãå¿ ãçŸããŸãã
ããããéçšããŠããSNSèªååç°å¢ã§ã2026幎8æ8æ¥ãã9æ¥ã«ãããŠã®2æ¥éã§ã5æ¬ã®ã¬ãŒã³ããåæã«ãªãã©ã€ä¿®æ£ãäžãã£ãŠããŸãããçç¶ã¯ã©ãã䌌ãŠããŸããããåå ã¯ãã©ãã©ã§ããã
note-autolike ã®ãªã©ã€ãã3æ¥éãŒãä»¶ã§ããã æçµæå㯠2026-08-06T09:03:20Zãèšäºã¿ã€ãã«ã«58åäžéã®ããªããŒã·ã§ã³ãããã®ã«ãçæããã³ããã«ã¯åæ°å¶éãäžèšãæžãããŠããªãã£ãã®ã§ãã¢ãã«ã¯æ¯å63åã®ã¿ã€ãã«ãè¿ããŠããã3åæããŠ3åãšã63åã4åç®ã¯è©Šãããããšãªã諊ãåŠçã«å ¥ããŸãã
ai-portraits ã®ç»åçæã63å空åãããŠããŸããã ã¢ãã¬ãŒã·ã§ã³ã§ãªã¯ãšã¹ãã匟ããããšããããã³ãããæ®µéçã«ç©åœã«ããŠå詊è¡ãããã©ãŒã«ããã¯ã3段å®è£ ããŠããŸããã1æŠå¿µãããæå€§21åã3æŠå¿µãªãæå€§63åã宿ž¬ã§ã¯ã察象ã³ã³ãã³ãèªäœãã¢ãã¬ãŒã·ã§ã³ã®åŒã£ãããåå ã®ãšããTierãäžããŠãå£ã¯åããŸããã3æ®µå šéšã詊ãããšããã§çµæã¯1段ç®ãšåãã§ããã
browser-slot.sh ã¯ã°ããŒãã«æ 3æ¬ã«å¯ŸããŠ1æéããã18ã34æ¬ãèµ·åã詊ã¿ãŸãã 1 runã¯14ã62åãããã®ã§æ ã¯åžžã«åãŸã£ãŠãããä¿®æ£åã®å®è£
ã¯ãacquire ã«å€±æããç¬éã« SKIP: global limit reached ãåºã㊠exit 0 ããŠããŸããããã°ã«æ®ã£ãŠããæ°åã¯ã09æå°9ä»¶ã»11æå°8ä»¶ã»12æå°12ä»¶ãèµ·åããããŠããªãã£ããšãããã®ã§ãããããã¢ã«ãŠã³ãã¯åœæ¥2åãšãskipã§ããããããã©ããŒã0ã§ããã
3ã€ãšãããã1åããå®è£ ãããŠããŸããããã©ããçŽã£ãŠããŸããã
倱æã®çš®é¡ãéãããã§ãã63åã®ã¿ã€ãã«ã¯ãåãããã³ãããåãã¢ãã«ã«æããã°åã63åãè¿ããŸããæ±ºå®è«çãªå€±æã§ããã¢ãã¬ãŒã·ã§ã³ãããã¯ããã³ã³ãã³ããåå ãªãæ¡ä»¶ãå€ããªããã°äœæ®µè©ŠããŠãå€ãããªããæ ã®äžè¶³ã¯ã誰ããæ ã空ãããŸã§åŸ ã€ãããªãã
ãã®åé¡ããªããªãã©ã€å®è£ ã§ã¯ã3çš®é¡ã®å€±æããã¹ãŠã3åã«ãŒãâçµäºããšããåäžã®åŠçã«åžã蟌ãŸããŸããçµæãçŽãã倱æãçŽããªã倱æãåæ°ã«ãŠã³ããããŠãæçµçã«åãåºå£ããåºãŠããŸãã
éåŽã®å£ãæ¹ããããŸããã倱æããããšãã°ã«æ®ã£ãŠããã®ã«ãå®éã«ã¯äžéæ§ã®é害ã ã£ãã±ãŒã¹ã§ãããããã® watch-arb ã¹ã¯ãªããã¯ãååããŒãžã®ååŸã«å€±æãããš extract_failed ãšã ãèšé²ããŠããŸãããç¿æç»é¢ãèŠããšãååŸã§ããªãåºã16ä»¶ããããšèŠãããå®éã«ã¯ HTTP 429 ãè¿ã£ãŠããã ãã§ãåãåºã1ä»¶ãã€ãã£ããå©ããããŒãã§ã¯æ£ããåããŠããŸãããåæéäžã«ç¢ºèªããé¡äŒŒäºè±¡ã䞊ã¹ããšãåã429ããã°äžã§7ã€ã®å¥ã®ååã§æ®ã£ãŠããŸããã
| ã¬ãŒã³ | ãã°ã«æ®ã£ãŠããåå | å®éã®çå |
|---|---|---|
| watch-arb ååããŒãž | extract_failed |
HTTP 429 |
| watch-arb bootstrap | FATAL |
undici ã®10ç§ ConnectTimeout |
| social-autolike ig-3 | coverage 0/11 â å
šãœãŒã¹ååŸå€±æ â exit 1 |
IG private API ã® 429 |
| social-autolike ig-sug | following mapååŸå€±æ â run忢 |
åã 429 |
| outreach-multi enrich | å
šä»¶ error â qualify å
šä»¶ drop â éç®0é |
web_profile_info ãåžžæ 429 |
| outreach-multi igJson | unparsable json |
ã¬ã¹ãã³ã¹ã512,558æåããã®ã«400,000æåã§slice |
extract_failed ãšèšé²ãããŠããã°ããã®åºã®æ§é ãç¹æ®ããšèªã¿ãŸããå®éã¯ãéãå©ããããããšããåé¡ã§ãç¿æ¥ãã£ããå©ãã°åãããååãééã£ãŠããã®ã§ãä¿®æ£ã®æ¹åãééãç¶ããŠããŸããã
ãããæŽçãããšãæ£ãããªãã©ã€èšèšã¯3ã€ã®åãã«çããããšã«åæããŸãã
(1) åŸ
ãŠã°çŽããã
(2) äœåã§èŠåããã
(3) 次ã®è©Šè¡ã«äœãå€ããŠæž¡ããã
ã倱æããããã1åãã¯ãã®3ã€ããã¹ãŠçç¥ããå®è£ ã§ããäžã®5æ¬ãåæã«æ¢ãŸã£ãŠããæã¯ã3ã€ãšãçããæžãããŠããŸããã§ããã
æå120äžã®èªååç°å¢ã¯ãæ¯æ¥ã³ãŒããæžããŠããããåããŠããã®ã§ã¯ãããŸãããèªåãå¯ãŠããéã«ç°å¢ãèªå·±ä¿®æ£ããŠåãç¶ããããæç«ããŠããŸãããã®åæã«ç«ã€ãšããªãã©ã€èšèšã®èŠä»¶ãå€ãããŸããã倱æããŠã次ã¯çŽããã§ã¯ãªããã倱æã®çš®é¡ããšã«æ¬¡ã®è¡åãæ±ºãããããšãèŠä»¶ã«ãªããŸãããã®èšèšãããå®è£ ãšç¡ãå®è£ ã§ã¯ãé害ã®åŸ©æ§ã«èªåãé¢äžããå¿ èŠããããã©ãããæ ¹æ¬çã«å€ãããŸãã
å šäœã®æµã
3ã€ã®åããã²ãšã€ã®å³ã§æŽçããŸãã
倱æãçºçãã
â
âŒ
âââââââââââââââââââââââââââââââââââââââ
â (1) åŸ
ãŠã°çŽããïŒ â
â â
â æ äžè¶³ â 空ããŸã§åŸ
〠â
â ãããã¯ãŒã¯æ â ããã¯ãªãããŠåè©Šè¡ â
â 429 â Retry-After åªå
ã§åŸ
〠â
â ãã°ã€ã³åã â åŸ
ã£ãŠãçŽããªã â
â ããªããŒã·ã§ã³éå â åŸ
ã£ãŠãçŽããªã â
â ã¢ãã¬ãŒã·ã§ã³ â æ®µã«ããïŒåŸè¿°ïŒ â
âââââââââââââââââââââââââââââââââââââââ
â åŸ
ã£ãŠãçŽããªã
âŒ
âââââââââââââââââââââââââââââââââââââââ
â (2) äœåã§èŠåããïŒ â
â â
â åæ°äžé or åä»ãã3æéè¶
â æã¡åã â
â ãŸã 詊ãã â (3)ãž â
âââââââââââââââââââââââââââââââââââââââ
â ãŸã 詊ãã
âŒ
âââââââââââââââââââââââââââââââââââââââ
â (3) äœãå€ããŠæž¡ããïŒ â
â â
â åãå
å®¹ãæž¡ã â æ±ºå®è«ç倱æãªãåãçµæ â
â ãšã©ãŒå
å®¹ãæ·»ããŠæž¡ã â ä¿®æ£ã§ãã倱æãªãå埩 â
â Tieréæ ŒïŒçžŠïŒâ æ¡ä»¶ãç©åœã«ããŠåè©Šè¡ â
â å¥èª²é¡ã«å·®ãæ¿ãïŒæšªïŒâ 課é¡ããšå€ãã â
âââââââââââââââââââââââââââââââââââââââ
3ç¯ã¯é çªã«åŠçããã®ã§ã¯ãªãã倱æã®çš®é¡ã«ãã£ãŠé©çšããçµã¿åãããå€ãããŸãããããã¯ãŒã¯æãªã(1)ã ãã§ååã§ããæ±ºå®è«çãªããªããŒã·ã§ã³éåã¯(1)ãã¹ãããããŠ(3)ããå ¥ããŸããã¢ãã¬ãŒã·ã§ã³ãããã¯ã¯(1)ã®ã¹ãããã»(2)ã®éŸå€ã»(3)ã®å€ãæ¹ãå šéšé¢ä¿ããŸãã
(1) åŸ ã€ â å®è£ ã®éªšæ Œ
browser-slot.sh ã®ä¿®æ£ã§å ããã³ã¢ã¯ä»¥äžã®åœ¢ã§ãã
BROWSER_SLOT_WAIT_SEC="${BROWSER_SLOT_WAIT_SEC:-600}"
wait_for_slot() {
local waited=0
while ! try_acquire_slot; do
local jitter=$(( RANDOM % 31 + 15 )) # 15ã45ç§ã©ã³ãã
sleep "$jitter"
waited=$(( waited + jitter ))
if [[ $waited -ge $BROWSER_SLOT_WAIT_SEC ]]; then
log "RESULT waited=${waited}s SKIP: slot timeout"
return 1
fi
done
return 0
}
å詊è¡ééã15ã45ç§ã®ã©ã³ãã ã«ããŸãã åºå®ééã«ãããšãæ ãåŸ ã£ãŠããå šãžã§ããåãç¬éã«æ®ºå°ããŸãïŒthundering herdïŒãã©ã³ãã ãžãã¿ãå ¥ããããšã§ããžã§ããäºãã«ãããŠèªç¶ã«åæ£ããŸãã宿ž¬ã§ãåºå®ééã§ã¯å šãžã§ããçŽååããŠããã£ãŠè©°ãŸãããšã確èªããŠãã倿ŽããŸããã
åŸ æ©äžã¯ã¬ãŒããæŽã¿ã£ã±ãªãã«ããŸããã ååŸå€å®ã®äžç¬ã ãããã¯ãæã£ãŠãå€å®åŸã¯å¿ ãé¢ããŸãããããééãããšåŸ æ©äžã®ãžã§ããå šéšããã¯ãæã¡åã£ãŠå šéšæ¢ãŸããŸãã
ãã°ã® RESULT è¡ã« waited=600s ãæ®ããŸãã ã600ç§åŸ
ã£ãããã§è«Šããããšã峿šãŠããã¯å¥ã®äºè±¡ã§ãåºå¥ã§ããªããšåŸããè©äŸ¡ã§ããŸããã
BROWSER_SLOT_WAIT_SEC=0 ã§ä¿®æ£åã®å³skipåäœãšå®å
šäžèŽããŸãã30æ¬ä»¥äžã®launchdãžã§ãããã®ã¹ã¯ãªãããå
±æããŠããã®ã§ãåŸæ¹äºæãç¶æããªããæ®µéçã«åãæ¿ããããããã«ããŸããã
äžæ¹ãäžéæ§ã«èŠããŠå®ã¯å³å詊è¡ã§çŽãã±ãŒã¹ããããŸããCloudflare ã® Authentication error [code: 10000] ã§3æ¥é£ç¶ãžã§ãã倱æããŠããæ¡ä»¶ãããããOAuth ã倱å¹ãã â 人éã®æäœãèŠããã«èŠããŠããŸããããšãããã·ã§ã«ãš launchd æå°ç°å¢ã®äž¡æ¹ã§ãããŒã5æ¬ãå®èµ°ãããšå
šãŠ exit 0ãå€±ææ¥ã®ãã°ãèªã¿çŽããšãã¢ã¯ã»ã¹ããŒã¯ã³åçºè¡çŽåŸã®0.5ç§åŸã«åäžããŒã¯ã³ã§å¥ãšã³ããã€ã³ãã 200 ãè¿ããŠãããããŒã¯ã³åçºè¡çŽåŸã®äžéæ§ 401 ã§ããªãã©ã€ã1åãç¡ãã®ãçå ã§ããã倱æç㯠3/9æ¥ïŒ33%ïŒããèªèšŒãšã©ãŒã ããäººéæ¡ä»¶ããšå³æãããšãæ©æ¢°ã§çŽãããã®ã人éã®åŸ
ã¡è¡åã«ç©ã¿ãŸãã
(2) 諊ãã â çµç«¯ãå¿ ã眮ã
ai-portraits ã® Tier ã©ããŒãäŸã«ããŸããã¢ãã¬ãŒã·ã§ã³ãããã¯ã®ãšã©ãŒã«ã¯ moderation_stage ãšãããã£ãŒã«ãããããŸããoutput ã§æ¢ãŸã£ããã®ã¯Tieréæ Œã§éãå®çžŸããããinput ããå§ãŸã£ããã®ã¯ãåæéäž9詊è¡å
šæ»
ã§ããã
for tier in [1, 2, 3]:
result = generate(concept, tier=tier)
if result.get("moderation_stage") == "input":
# input段 â Tierãå€ããŠãå£ã¯åããªããæ®ããbreak
log(f"input-stage block, skip remaining tiers for this concept")
break
if result.get("moderation_blocked"):
# output段 â Tieréæ Œã§éãå¯èœæ§ããã
continue
if result.get("success"):
return result
梯åãéãã䟡å€ããããã1段ç®ã®ãšã©ãŒæ¬æã§å€å®ããŸãã moderation_stage == "input" ã§ break ããã ãã§ã63åã®ç©ºèµ°ãã0åã«ãªããŸããåæ§ã«ãgsheets_retry.py ã§ã¯ 401/403/404 ã¯å³ raise ããããã«èšèšãããŠããŸãã
RETRYABLE_HTTP = {429, 500, 502, 503, 504}
def sheets_call_with_retry(fn, max_attempts=3):
delay = 2
for attempt in range(max_attempts):
try:
return fn()
except HttpError as e:
if e.resp.status not in RETRYABLE_HTTP:
raise # 401/403/404 ã¯å³ raise â ãªãã©ã€ã§é ããªã
if attempt == max_attempts - 1:
raise
time.sleep(delay)
delay *= 3 # 2ç§ â 6ç§ïŒåèšæå€§8ç§ïŒ
except (socket.timeout, ConnectionError, TimeoutError):
if attempt == max_attempts - 1:
raise
time.sleep(delay)
delay *= 3
ãåèšæå€§8ç§ã絶察ã«è¶ ããªãããå ã«æ±ºããŸãã ææ°ããã¯ãªããåæ°ã§æ±ºãããšãåŒã³åºãåŽã®ã¹ãããæéãéãã«é£ããŸããç§æ°ã§äžéãåºå®ããŠããåæ°ãéç®ããé çªãå®å šã§ãã
åæ°ã ãã§ã諊ããããå®è£
ãããšãæå»è»žã®é害ã«å¯Ÿå¿ã§ããŸãããkotonoha ã®ãžã§ããã¥ãŒã§ 39æé pending ã«ãªãäºæ
ããããŸãããäžé6åã§äžæããŠã requeue ãæåããã®ã§å€±æãäžåºŠãç«ãããç£èŠã鳎ãããå©çšè
ã®ç»é¢ã¯ãäœæäžãã®ãŸãŸã§ããã
ä¿®æ£åŸã®å€å®åŒã¯ãããªããŸãã
function shouldGiveUpForQuota({ quotaHits, createdAtMs, resumeAtMs }) {
const THREE_HOURS_MS = 3 * 60 * 60 * 1000;
return (
quotaHits >= 2 || // 2åç®ã®äžé
resumeAtMs > createdAtMs + THREE_HOURS_MS // åä»ãã3æéè¶
);
}
ãåæ°ããšãåä»ããã®çµ¶å¯Ÿæå»ãã®äºéã§æã¡æ¢ãã眮ããŸãã äºæ
åœæ¥ã®å®å€ïŒåä» 2026-08-19T00:36:57Zã»åé 06:37:00ZïŒãå¢çã±ãŒã¹ãšããŠãã¹ãã«å
¥ãããšãåãäºæ
ã¯äºåºŠãšéããªãããšãæ©æ¢°ã§ä¿èšŒãããŸãã
(3) æ»ã â æ¬¡ã®è©Šè¡ã«å€ããå å®¹ãæž¡ã
note-autolike ã®äŸã§ãããªã©ã€ãã3æ¥éãŒãä»¶ã ã£ãåå ã¯ãåäžããã³ããã3åæãçŽãã ãã®å®è£
ã§ããããšã©ãŒãæ©æ¢°å¯èªã«ããŠã2åç®ä»¥éã®è©Šè¡ã«ã¯ä¿®æ£æç€ºãä»ããŠæž¡ããŸãã
// ãšã©ãŒã« code / actual / limit ãèŒãã
throw new ValidationError(`ã¿ã€ãã«ã¯1ã58åã«ããŠãã ããïŒå®é: ${actual}åïŒ`, {
code: "title-too-long",
actual,
limit: 58,
});
// 2åç®ä»¥éã¯ä¿®æ£æç€ºãä»ããŠæãã
function rewriteRetryInstruction(error) {
if (error.code === "title-too-long") {
return [
`ååã®åºåã¯æ¬¡ã®çç±ã§äžæ¡çšã§ããïŒ`,
`ã¿ã€ãã«ã${error.actual}åãããäžéã®${error.limit}åãè¶
ããŠããŸãã`,
`åãå
容ã»åãæ§æã®ãŸãŸããã®ç¹ã ããæºãã圢ãžçŽããŠãçJSONã®ã¿ãååºåããŠãã ããã`,
`ã¿ã€ãã«ã¯çµµæå蟌ã¿ã§${error.limit}å以å
ïŒååã¯${error.actual}åïŒã«å¿
ãåããŠãã ããã`,
].join("\n");
}
}
async function askClaudeWithRetry(prompt, retries = 3) {
let currentPrompt = prompt;
for (let i = 0; i < retries; i++) {
try {
const result = await callClaude(currentPrompt);
validateRewrite(result);
return result;
} catch (e) {
if (e instanceof ValidationError) {
// 決å®è«çãªå€±æ â ã¹ãªãŒããªããä¿®æ£æç€ºãä»ããŠå³å詊è¡
currentPrompt = prompt + "\n\n" + rewriteRetryInstruction(e);
} else {
// ããã»ã¹ç³»ã®å€±æ â 30ç§åŸ
ã€
await sleep(30_000);
}
}
}
}
決å®è«çãªå€±æã¯ã¹ãªãŒããæãŸãå³å詊è¡ããŸãã ããªããŒã·ã§ã³éåã«2ç§åŸ ã€ã®ã¯ã2ç§ãæšãŠãŠããã ãã§ãããåŸ ã€ããšãçŽããŠæãçŽãããåããªãã©ã€ã§æ··ãããšãçŽãã倱æã«ãåŸ ã¡æéãæããŸãã倱æã®çš®é¡ã§åå²ããŠãã¹ãªãŒããå ¥ããå Žæãéžã³ãŸãã
LLMãžè¿ãä¿®æ£æç€ºã¯ãæ©æ¢°ã®èå¥åãã§ã¯ãªãã人éèªã®æç€ºïŒçŸç©ã®æååãã§æž¡ããŸãã"title-too-long" ããã®ãŸãŸæž¡ããš3飿ããŸãããã¿ã€ãã«ã63åããäžéã®58åãè¶
ããŠããŸãã58å以å
ã«åããŠãã ããããšæž¡ããšå埩ããŸããããã¯å¥ã®ã¹ã¯ãªããã§å
ã«å®èšŒæžã¿ã®äºå®ã§ãã
3ç¯ã®å šå®¹ã¯ããã§äžæŠæ¢ããŸããæ¬¡ã¯åç¯ã®ãå£ãæ¹ããšãçŽãæ¹ããã5ã¬ãŒã³åæé害ããæŸã£ãã±ãŒã¹å¥ã«åè§£ããŠãããŸãã
å®è£ ã®è©³çް
ããªãã©ã€ãæžãããã¯å®äºã§ã¯ãªã ââ ãã©ã«ãæ³šå ¥ãåãã³ãããã«å ¥ãã
ååã§èšèšãã3ç¯ã¯ãæžããæç¹ã§ã¯ãåãã¯ãã®ã³ãŒããã§ãããããŸããã鿬 é害ã¯åçŸææ®µããªãã®ã§ããªãã©ã€ããã®çµè·¯ãå®éã«éããã©ããã確ãããæ¹æ³ãããã©ã«ãã§ã¯ååšããŸãããæåã®4æ¬ã®å®è£
ã¯ããã§çµãã£ãŠããŸãããgit diff ãèŠãŠç¢ºèªããæ°ã«ãªã£ãŠãå®äºã«ããŠããŸããã
å®éã«å¹ããã®ã¯ããã©ã«ãæ³šå ¥ãå®è£ æ¬äœãšåãã³ãããã«å梱ããããšã§ããã
// watch-arb/collector/lib/api.mjs
let _failOnceRemaining = parseInt(process.env.WA_API_FAIL_ONCE ?? '0', 10);
async function req(path, opts = {}) {
if (_failOnceRemaining > 0) {
_failOnceRemaining--;
throw new TypeError('fetch failed'); // åæã®éå®³ãæ¬çªã§ã¯çµ¶å¯Ÿã«çºç«ããªã
}
// ... æ¬æ¥ã®fetchåŠç
}
ã«ãŠã³ã¿ã¯ã¢ãžã¥ãŒã«ã¹ã³ãŒãã§å
š req åŒã³åºãå
±éã§ããããã»ã¹èµ·ååŸã®æåã®Nåã ããfetch ãåŒã¶åã«åæã® TypeError ãæããŸãã
å®äºæ¡ä»¶ã¯2æ¬ã»ããã§å¿ ãäž¡æ¹ç¢ºèªããŸãã
-
â
WA_API_FAIL_ONCE=2ã§èµ·åããŠapi retry attempt=1/3ãš2/3ãåºãããã§ FATAL ãªã exit 0 ã§å®èµ°ãã - â¡ ç°å¢å€æ°æªèšå®ãªã retry ãã°ã1è¡ãåºãªã
â¡ãããããšã§ããã©ã«ãæ³šå ¥ãæ¬çªã«æŒããŠããªããããšãåãã³ãã³ãã§ç€ºããŸãã宿ž¬ã§åºããã°ã¯ãããªããŸããã
[INFO] api retry attempt=1/3 path=/api/collector/shops reason=fetch failed
[INFO] api retry attempt=2/3 path=/api/collector/shops reason=fetch failed
[INFO] 32ä»¶ã ingest ã㊠exit 0
ãã®å㯠watch-arb ãã zaiko-radarã»lily-line-funnelã»social-autolike ãšæšªå±éããŠ4æ¬ã«ãªããŸãããããªãã©ã€ãæžãããã¯ããã©ã«ã泚å
¥èŸŒã¿ã§èµ°ã£ããšãããèŠããã§ãªããã°å®äºã«ããªãããšããã«ãŒã«ã«å€ãããŸããã
å詊è¡ãã察象ã1颿°ã«éãã
ãªãã©ã€ãæžããšããæåã«æ±ºããããšã¯ãäœå詊ãããã§ã¯ãªããäœãå詊è¡ããããã§ãã
// å€å®ã1颿°ã«éçŽãã
function isTransientStatus(status) {
return status === 429 || (status >= 500 && status <= 504);
}
async function req(path, opts = {}) {
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
try {
const res = await fetchWithTimeout(path, opts);
if (!res.ok) {
if (!isTransientStatus(res.status)) throw new HttpError(res.status);
if (attempt === MAX_ATTEMPTS) throw new HttpError(res.status);
await sleep(backoff(attempt));
continue;
}
return await res.json();
} catch (e) {
if (!isNetworkError(e)) throw e; // ãããã¯ãŒã¯äŸå€ä»¥å€ã¯å³throw
if (attempt === MAX_ATTEMPTS) throw e;
await sleep(backoff(attempt));
}
}
}
401/403/404 ãå³ throw ããã®ã¯çååã§ã¯ãªããå¥ã®äºæ ãé²ãããã®èšèšã§ããæš©éãšã©ãŒããªãã©ã€ã§å ããšãåããªã¯ãšã¹ãã3åæããŠ3åãšãåã倱æãè¿ããŸãããã°ã«ã¯ã3å倱æããããšã ãæ®ãããæš©éãç¡ãããšããçå ã¯åãããŸãã
transient-failure-recorded-as-permanent.md ã§èšé²ããã7ã€ã®ååã§æ®ã£ã429ããšéåãã®äºæ
ã§ãã429ã extract_failed ã«åããã®ãšåãæ§é ã§ã401/403 ããããŸã«é
ããã«åããŸãã
isTransientStatus ã1颿°ã«åºãçç±ã¯ã倿ãè€æ°ã®åŒã³åºãç®æã«æ£ããšå Žæããšã«åŸ®åŠã«éãå®çŸ©ãçãŸããããã§ããgsheets_retry.py ã® RETRYABLE_HTTP = {429, 500, 502, 503, 504} ãšãã宿°ã»ãããåãçºæ³ã§ããã©ã®ã¹ããŒã¿ã¹ããªãã©ã€å¯Ÿè±¡ãããšããç¥èã1ç®æã«éããŠããŸãã
ãªãã©ã€ãå ¥ãããšäŸå€ã®äŒããæ¹ãå€ãã
kotonoha ã« withRetry ãå®è£
ããç¿ã³ãããã§ãå¯äœçšã®ä¿®æ£ã3ä»¶å
¥ããŸããããæ©èœã远å ããããšæã£ãŠããŸããããå®éã«ã¯ãäŸå€ã®äŒããæ¹ãå€ããã倿Žã§ããã
倿Žåãpurge() 㯠5xx ãè¿ã£ãŠãåŸç¶ã®ãæå
ã®äœæ¥ãã£ã¬ã¯ããª30æ¥æé€ããŸã§åŠçãé²ãã§ããŸãããres.ok ã false ã§ã次ã®è¡ãžé²ãå®è£
ã«ãªã£ãŠããããã§ããwithRetry ãå
¥ããŠ5xx ã®ç¬éã« throw ããããã«ãªããšãæé€ã®ã³ãŒããããã§å°éäžèœã«ãªããŸããå®å®³ã¯ãäœæ¥ãã£ã¬ã¯ããªã30æ¥åèç©ããããšããã宿çã«æåæé€ããªããã°æ°ã¥ããªãçš®é¡ã®éããªå£åã§ããã
async function purge(jobId) {
try {
await uploadResult(jobId);
} catch (e) {
log(`purge upload failed: ${e.message}`);
// ããã§ throw ããªã â 眮ãå Žã®å€±æã¯èšé²ããŠç¶è¡ãã
}
await cleanWorkDir(); // äŸå€ã®æç¡ã«ãããããå¿
ãèµ°ããã
}
ãã1件㯠setContentSafely ã§ã1åç®ãš2åç®ã®å詊è¡ã®éã0ç§ã§ããããªãœãŒã¹éŒè¿«ã§èœã¡ãçŽåŸã«åæ¡ä»¶ã§å³å詊è¡ããŠããå§åãæ®ã£ãŠããéã¯åãçµæã«ãªããŸããååã§ã決å®è«çãªå€±æã¯ã¹ãªãŒããæãŸãå³å詊è¡ããšæžããŸãããããã®éåŽã§ããè³æºéŒè¿«ãåå ã®å€±æã¯ãåŸ
ããªããšãªãã©ã€ã«æå³ããããŸããã
詊è¡åæ°ã¯ã倱æãããšãäœãèµ·ããããã§é察称ã«èšå®ããŠããŸããreport()ïŒçµæå ±åïŒã¯å€±æãããšå©çšè
ã®ç»é¢ããå®è¡äžãã®ãŸãŸæ°žä¹
ã«æ¢ãŸãã®ã§ attempts=5ãclaim()ïŒäºçŽååŸïŒã¯ attempts=3ãäžåŸã®åæ°ã¯ã³ã¹ããšè¢«å®³ã®é察称ãç¡èŠããŸããã¿ã€ã ã¢ãŠããæäœã®éãã§åããŠããŠãputObject ãš getStream ã¯120ç§ãgetText ãš listKeys ã¯30ç§ã§ããã¿ã€ã ã¢ãŠãèšå®ã®ãªã fetch ã¯ãæ°žä¹
ã«åãããããšããææªã®ãªãã©ã€äžèœç¶æ
ãäœããŸãã
ã¹ããªãŒã ãæž¡ããšãªãã©ã€ã§ããªã
å®è£ ãããŸã§æèããŠããªãã£ãå¶çŽããããŸãããªãã©ã€å¯èœã«ããã«ã¯ãå ¥åããåå®è¡å¯èœãã§ãªããã°ãªããŸããã
// NG: ã¹ããªãŒã ã¯äžåºŠæ¶è²»ãããåçã§ããªã
async function retryWithStream(stream) {
for (let i = 0; i < 3; i++) {
await fetch(url, { body: stream }); // 2åç®ã¯ã¹ããªãŒã ã空
}
}
// OK: Buffer ã string ã«éå®ãã
async function retryWithBuffer(buffer) {
for (let i = 0; i < 3; i++) {
await fetch(url, { body: buffer }); // äœåºŠã§ãåãããŒã¿ãéãã
}
}
putObject ã«æž¡ã body ã Buffer ã string ã«éå®ããŸãããåå¶çŽãã³ã¡ã³ãã§æ®ããŠãããªããšãåŸããåŒã³åºãåŽãã¹ããªãŒã ãæž¡ããŠããªãã©ã€ãåããŠããã®ã«æ¯å倱æããããã°ã«ãªããŸããã¹ããªãŒã ããªãã©ã€ã«æž¡ããš2åç®ä»¥éã¯ç©ºã®ããã£ãéãç¶ããŸãããšã©ãŒãåºãã«æ¯å倱æããŸãã
ç§ãè©°ãŸã£ã話
é²åŸ¡ã®ããã«çœ®ãã宿°ããæåç0%ã®å£ã«ãªã£ãŠãã
outreach-multi ã® DM åä¿¡ç®±ããŒã¹åŠçã§ãunparsable json ãšãããšã©ãŒãæ¯æ¥åºãŠããŸãããã»ã¬ã¯ã¿ãå£ããããJSON ã®æ§é ãå€ãã£ãããšçã£ãŠ3æ¥éä¿®æ£ãç¶ããŸããã
宿
ã¯ãåä¿¡ç®±ååŸã®ã³ãŒãã« text.slice(0, 400_000) ãšããè¡ããããŸããã巚倧ãªã¬ã¹ãã³ã¹ãæ¥ããšãã«ã¡ã¢ãªãä¿è·ããããã«æžãããäžéã§ãããIG ã® DM åä¿¡ç®±ã®å®ãµã€ãºãèšæž¬ããã 512,558æå ã§ãããäžéã 400,000 ãªã®ã§ãæ¯åå¿
ãéäžã§åããŸããåãã JSON 㯠JSON.parse ã§æ¯åå¿
ã倱æããŸããæåçã¯0%ã§ãã
3æ¥éåã倱æãèŠç¶ããŠããã®ã¯ãtext.slice ã®è¡ãå¿ããŠãããŒã¹ã«å€±æããŠãã â æ§é ãå£ããããšèªãã§ããããã§ããçç¶ãåºå§ããã®ãã仿¥ãããã§ã¯ãããŸããã§ãããåä¿¡ç®±ã®ãµã€ãºã 400,000åãè¶
ããæ¥ããéãã«å€±æãå§ããŠãããæ¥æ°ã¥ãããéç®0ä»¶ã«ãªã£ãŠãã圢ã§ãã
ä¿®æ£ã¯ page.evaluate ã®äžã§ JSON.parse ãŸã§å®äºããã倱æãããšãã ãå
é 300åã蚺æçšã«è¿ã圢ã«å€ããŸããã
ãã®ä»¶ããåŸãèšèšã«ãŒã«ã¯ãäžéã眮ããªããäžéã«åœãã£ãããšèªäœããã°ã«åºããã§ããtext.slice ã¯ãµã€ã¬ã³ãã«åãèœãšãã ãã§ãåããããšã誰ã«ãç¥ãããŸãããé²åŸ¡ã®å®æ°ããã®å®æ°ãè¶
ããç¬éããæä¹
çãªå€±æã«å€ãã£ãŠããŠãããã°ã«ã¯äœãæ®ããŸããã
quarantine ãæ°žä¹ ã«ãŒãã«ãªã£ãŠãã
unfollow-core.js ã®ã¢ã³ãã©ããŒåŠçã«ã3å倱æãããš7æ¥éã®ä¿çïŒquarantineïŒã«å
¥ãä»çµã¿ããããŸããã
åé¡ã¯ãquarantine ãã埩掻ãããã©ããªãããã§ãã埩掻ããã¢ã«ãŠã³ãã¯åè£ãªã¹ãã«æ»ããŸããå床3å倱æãããšããŸã7æ¥éã®ä¿çã«å ¥ããŸãããããç¡éã«åããŸãã
åè£ã 2,605ä»¶ãã£ãŠãscanned: 8 / unfollowed: 2 ãšããæ°åãèŠãŠ2é±éæ©ã¿ãŸãããåè£ãã»ãŒåŠçãããŠããªããããªãé²ãŸãªãã®ããã調ã¹ãããåãã¢ã«ãŠã³ãã quarantine ã«å
¥ã£ãŠã¯åºããç¹°ãè¿ããŠããŠãåã«é²ãã§ããªããã§ããã
ãã°ã«ã¯ circuit-break ãšåºãŠããŸãããcircuit-break ãæ©èœããŠããã®ã«å®è¡æ°ãå¢ããªãã®ã¯ççŸããŸãããã£ãšæ©ãæ°ã¥ããã¯ãã§ãããããcircuit-break ãåããŠãã = ä¿è·ãããŠããããšèªãã§ããã®ã§çããŸããã§ããã
ä¿®æ£ã¯ã环èšå€±æåæ°ïŒUNFOLLOW_GIVEUP_FAILS = 6ãquarantine 倱æéŸå€ã®2åïŒã«éããã¢ã«ãŠã³ããå°åž³ããç©çåé€ããããšã§ããã
ãä¿çãã¯æºãŸãã ãã§æžããŸããã æšãŠãçµç«¯ãå¿
ã眮ããªããã°ãå°åž³ã¯åŠçã§ããªãã¢ã«ãŠã³ãã§åãŸãç¶ããŸãããã ãæšãŠãåã«ãèªåã®åŠçãæ£åžžã«åããŠããããã®ç¢ºèªãå
ã§ããã»ãã·ã§ã³ãåããŠããã ãã§ãæ£åžžãªã¢ã«ãŠã³ããå
šéšæšãŠãŠããŸãéäºæ
ãããåŸãŸããkotonoha ã®ããŒãžã«æžãããæšãŠãåã«èªåã®æ£åžžæ§ã確èªããããšããååãããã«ãé©çšãããŸãã
ç£èŠã39æé鳎ããªãã£ã
kotonoha ã®ãžã§ãã39æéãã£ãšãäœæäžãã®ãŸãŸæ¢ãŸã£ãŠããŸãããç£èŠã¯äžåºŠã鳎ããŸããã§ããããžã§ãã¯å€±æãšããŠèšé²ãããŠããŸããã§ããã
åå ã¯ãåæ°ãç¶æ ã«æã£ãŠããªããªãã©ã€ã«ã¯ãæã¡æ¢ãã眮ããªããã§ããã
ãžã§ããã¯ã©ãŒã¿äžéã«åœãããš requeue ã§ pending ã«æ»ããŸããæ»ãããžã§ãã¯ãäœåç®ãããšããã«ãŠã³ã¿ãæã£ãŠããŸããã§ããã7åç®ã®ãžã§ãã1åç®ã®ãžã§ããå®è£
ããåºå¥ã§ããŸãããrequeue ãæåããã®ã§å€±æãç«ã¡ãŸãããç£èŠã®éŸå€ã«ãåœãããŸãããå©çšè
ã®ç»é¢ã«ã¯ãäœæäžããå»¶ã
ãšè¡šç€ºããç¶ããŸãã
ä¿®æ£ã¯2段éã§ããããŸããåæ°ãç¶æ
ã«æãããããæ¬¡ã«ãåæ°ãšåä»ããã®çµ¶å¯Ÿæå»ã®äºéã§æã¡æ¢ãã眮ãããååã§ç€ºãã shouldGiveUpForQuota 颿°ãå¢çã±ãŒã¹ä»ãã®ãã¹ãã«éããšããäºæ
åœæ¥ã®å®å€ïŒåä» 2026-08-19T00:36:57Zã»åéäºå® 06:37:00ZïŒã1ã±ãŒã¹å
¥ããŸãããåãäºæ
ã¯äºåºŠãšéããªãããšãæ©æ¢°ã§ä¿èšŒãããŸãã
åæ°ã ãã§æã¡æ¢ãã眮ããŠãããšã1åç®ã ãåéã12æéå ããéããŠããŸããŸããæå»ã ãã ãšãåéã¯çããäžéãé£ç¶ããŠããããéããŠããŸããŸããä»åã®äºæ ã¯æå»æ¡ä»¶ã®åŽã§1åç®ã«æãŸã圢ã§ãããäºéã«ããæå³ãå®ããŒã¿ã§æž¬ããä»¶ã§ãã
ãã¯ãŒã«ãŒãåŸ ã€ããšããã®ãžã§ãã諊ãããã¯å¥ã®å€æã§ããã¯ã©ãŒã¿ã¯åŸ ãŠã°çŽã倱æãªã®ã§ã¯ãŒã«ãŒã¯åŸ ã¡ãŸããã§ãå©çšè ã3æé以äžåŸ ãããæ ¹æ ã«ã¯ãªããŸããã
ãèªèšŒãšã©ãŒããšå³æããŠ3æ¥é人éåŸ ã¡ã«ããŠãã
com.lily.line-pdca ã Cloudflare ã® Authentication error [code: 10000] ã§3æ¥é£ç¶æ¢ãŸããŸãããèªèšŒãšã©ãŒãªã®ã§ãOAuth ã倱å¹ãããæåã§ wrangler login ãèŠãããšå€æããŸãããç°å¢å€æ°ã®èšå®æŒããçããŸããã
確ãããåã«ã人éã®GUIæäœãèŠãæ¡ä»¶ããšããŠåé¡ããã®ãééãã§ããã
å®éã« shell ãš launchd æå°ç°å¢ã®äž¡æ¹ã§ wrangler whoamiã»kv key listã»deploy --dry-run ãå®èµ°ãããšå
šãŠ exit 0 ã§ãããåºåãã€ãæ°ãŸã§åäžïŒ1,516 bytesïŒãå€±ææ¥ã®ãã°ãèªã¿çŽããšãã¢ã¯ã»ã¹ããŒã¯ã³ãåçºè¡ãã 0.5ç§åŸ ã« KV keys ã 401 ãè¿ããŠããŸããããã®åãããŒã¯ã³ã§0.5ç§åŸã® /user 㯠200 ãè¿ããŠããŸãããããŒã¯ã³åçºè¡çŽåŸã®äžéæ§ 401 ã§ããªãã©ã€ã1åããªãã®ãçå ã§ããã
倱æç㯠3/9æ¥ïŒçŽ33%ïŒã§ãããæ¯æ¥åºãªããã§ã3æ¥ã«1åã¯åºãããã®é »åºŠããäººéæ¡ä»¶ã«èŠãããåå ã§ããããŸããã
倿ãå€ããåºæºã¯ãåãæé ãä»ããäžåºŠå©ãããéããããå®éã«å©ããŠç¢ºãããããšã§ããããšã©ãŒã®çš®é¡ïŒ401ã»Authentication errorïŒã ãã§ã絶察ã«çŽããªãããšåé¡ããªããtransient-failure-recorded-as-permanent.md ã«èšé²ããã429ã7ã€ã®ååã§æ®ã£ãŠããããšåãåã§ãä»åºŠã¯ãèªèšŒãšã©ãŒããšããååãäžéæ§ã®å€±æãäººéæ¡ä»¶ã«èŠããŠããŸããã
ãã®ä»¶ä»¥éãèªèšŒãšã©ãŒãåºãããããŒãã1æ¬èµ°ãããŠãã倿ããã«ãŒã«ã«å€ãããŸããããæ©æ¢°ã§çŽãããã®ã人éã®äœæ¥åŸ ã¡ãã¥ãŒã«ç©ãŸãªããã®åæã¯ãæ©æ¢°ã§å©ããŠç¢ºããããã§ãã確ãããªããŸãŸå³æããã®ã¯ãå©ããã«ãçŽããªãããšæ±ºãã€ããŠããã®ãšå€ãããŸããã
ã€ãŸãããã€ã³ã
åæ®µã§åãäžããåå¥äºäŸã®è£ã«ããæ§é çãªãã¿ãŒã³ããŸãšããŸãããè©°ãŸã£ãå Žæã®ååããéãã ãã§ãæ ¹ã£ãã¯åãåã§ãã
倱æããã²ãšã€ã®ååãã«å§çž®ããŠèšé²ãã
watch-arbã®ååããŒãžã¯extract_failedãbootstrapå®è¡ã¯FATALãDMåä¿¡ç®±ã¯unparsable jsonãšèšé²ãããŠããŸããã3ã€ãšãå®éã®çå ã¯HTTP 429ã§ããtransient-failure-recorded-as-permanent.mdã«èšé²ãããã®äºè±¡ãæŽçãããšãåæéäžã§åã429ã7çš®é¡ã®å¥åã§æ®ã£ãŠããŸãããååã®æç¹ã§HTTPã¹ããŒã¿ã¹ãæ¶ãããšããåŸ
ãŠã°çŽãããªã®ããæ§é ãçŽãå¿
èŠããããã®ãã®å€æãã§ããªããªããŸããä¿®æ£ã¯1è¡ã§ããèšé²ããåã«429/503 â rate_limited / 401/403 â auth_error / ããä»¥å€ â extract_failedãšåé¡ããŸãããã®3åå²ããåŸ
ã€ã»è«Šããã»æ»ããã®åå²ã®æåã«ããåææ¡ä»¶ã§ãã
ãšã©ãŒæ¬æãæšãŠãŠãã
ai-portraitsã®generate.pyãcodex execã®stdout/stderrãæšãŠãŠãããããç»åãåºãªãã£ãçç±ãmissingãšããæ®ããŸããã§ãããmoderation_blockedãsafety_violations: ["sexual"]ãèŠããªããã°ãTieréæ Œã§å埩ããã®ãinput段ã§å³æã¡åãã¹ããã®å€æãç«ã¡ãŸããããšã©ãŒãä¿æããªãå®è£
ã§ã¯ãåŸ
ã€/諊ãã/æ»ããã®3ç¯ãããããæç«ããªãã®ã§ããšã©ãŒä¿æã¯èšèšã®åæã§ãã£ãŠæ©èœè¿œå ã§ã¯ãããŸããã
moderation_stageã確èªããã«å
šTierã詊ã
moderation_stage: inputã§æ¢ãŸã£ãæŠå¿µã¯ãT1ããT3ãŸã§å
šæ®µè©ŠããŠ9詊è¡å
šæ»
ã§ãããmoderation_stage: outputã§æ¢ãŸã£ããã®ã¯Tieréæ Œã§éãå®çžŸããããŸãïŒpartial 01_feet_up_windowã¯T2ã03_legs_on_bed_topdownã¯T3ïŒãT1ã®moderation_stageãã£ãŒã«ã1èªã§ã梯åãéãã䟡å€ããããããæ±ºãŸãã®ã«ããã£ãŒã«ããèªãŸãã«å
𿮵ã詊ããš1æŠå¿µãããæå€§21åã3æŠå¿µã§æå€§63åãç¡é§ã«ãªããŸãããå®å
šåŽãã©ãŒã«ããã¯ãäœæ®µããããããã1段ç®ã®ãšã©ãŒæ¬æã§éãã䟡å€ããããããå
ã«å€å®ããŸãã
éãå ã®å®¹éãæž¬ããã«é ç·ãã
social-autolikeã®follow-source-followersã§ãã©ãŒã«ããã¯ã远å ããåã«ãUIã¢ãŒãã«ã®followingåŽã408ä»¶/63ç§ã§äŒžã³ãã®ã«å¯ŸããŠfollowersåŽã12ä»¶ã§æ¢ãŸãããšãå
ã«å®æž¬ããŸããã容éãããåŽã ãããã©ãŒã«ããã¯ã«æ¡çšããã®ãçç±ã§ããæž¬ããã«é
ç·ãããšã容éã®ãªãéãå
ã«å
šãã©ãã£ãã¯ãæµããŠãéãå
ã远å ããã®ã«æåçãå€ãããªãããšããç¶æ³ã«ãªããŸããoutreach-multiã®pk解決ããIGã70ä»¶ååŸã§çµããšããæ¢ç¥ã®å£ãç¹ã蟌ãã§1run 30ä»¶å¶éã«ããŠããŸãã
æä¹ çãªå€±æããããš1åãã®æ ã«æ°ãã
outreach-multiã®DMéä¿¡ã§no ã¡ãã»ãŒãž buttonïŒDMäžå¯ã®èšå®ïŒãè¿ããšãexhausted after 3 attemptsã«ä¹ã£ãŠéä¿¡æ ã1ä»¶ããã3ååæ¶è²»ããŠããŸãããçžæã®ã¢ã«ãŠã³ãèšå®ã¯ãã¡ãããªãã©ã€ããŠãå€ãããŸãããsent.jsonlã®reasonãã£ãŒã«ããèªãã§ãno ã¡ãã»ãŒãž button / profile gone / ãã®ããŒãžã¯ååšããŸãããå«ãå®å
ã¯permanentlyFailed Setã«å
¥ããshouldSkip()ã§exhaustedã®æåã«å€å®ã眮ããŸããããã¯å段ã§è§Šãããäžéæ§ãæä¹
ãšããŠèšé²ããéåãã®äºæ
ããšåãæ ¹ã§ã倱æã®åé¡ã®èšèšããªãã©ã€åæ°ã®èšèšããå
ã«èŠããŸãã
429ã®ããšãåãé床ã§å©ãç¶ãã
social-autolikeã®ig-3ã¯0.3ã0.5ç§ééã§17ãœãŒã¹ãæ°ç§ã§æã¡èŸŒãã§14æéãŒãææã§ããã429ãåããåŸãééãå€ããªããã°ãå¶éäžã«åããªã¯ãšã¹ããç©ã¿äžããŠèªåã§ã¹ããããªã³ã°ãå»¶é·ããŸããretry-and-giveup-design.mdã«èšé²ããä¿®æ£ã§ã¯ã3é£ç¶429ã§ãã®ããã»ã¹ã®APIåŒã³åºããæã¡åãã©ãããšã429ã1床ã§ãèŠãåºã®concurrencyã1ã«èœãšãèšèšãå
¥ããŠããŸããåŸ
ã£ãŠããå詊è¡ããã ãã§ãªããé床ãã®ãã®ãèœãšãããšãã»ããã§ãã
ãªãã©ã€ã2å±€ã«éãªã£ãŠãã
watch-arbã®ingestShopãç¬èªã«3åã«ãŒããæã¡ãreq()åŽã«ã3åã«ãŒãããããŸãããææª3Ã3=9åã«ãªãããã°ã«ãretry attempt 3/3ãã2床䞊ãã§ãäžéãã©ãã«ããã®ããä»äœåç®ãªã®ããèªããŸããããã©ã«ã泚å
¥ãå
¥ããŠããã©ã¡ãã®å±€ãåããŠãããããåãããªããªããŸããä¿®æ£ã¯ingestShopåŽã®ç¬èªã«ãŒããåé€ããŠreq()ã®1å±€ã«äžæ¬åããŸãããåŒã³åºãåŽã¯å³throwããã ãã«ããŸãã
ãã©ãŒã«ããã¯ã®æåãæã¡åãã«ãŠã³ã¿ã«æ°ãã
ã3é£ç¶429ã§APIåŒã³åºããæã¡åããã©ããããUIãã©ãŒã«ããã¯ãæåããä»¶ãã«ãŠã³ãã«å«ããŠããŸãããéãéãäœã£ãŠã3ä»¶ã§æ¢ãŸã£ãŠããŸãããæã¡åãã«ãŠã³ã¿ã¯ãå šçµè·¯ã倱æãããšããã ãå¢ãããŸãããã©ãŒã«ããã¯å ã§æåããä»¶ã¯ãã¡ã€ã³ã®å€±æã«ãŠã³ãã«èŒããŸããã
ãåããªãã£ãïŒç°åžžïŒããšãåãã«è¡ãæéããªãã£ãïŒæ£åžžãªæã¡åãïŒããåããŒãä»¶ã§å€å®ãã
follow-source-followersã¯ãœãŒã¹ååŸã ãã§æéäºç®ã䜿ãåã£ãŠåè£ãŒãä»¶ã«ãªã£ãå Žåãæ¢åã®ãå
šãœãŒã¹ååŸå€±æâ誀æäœé²æ¢ã§åæ¢ãçµè·¯ã«å
¥ã£ãŠããŸããŸããåããªãã£ãã®ãæéããªãã£ãã ããªã®ãã¯å¥ã®äºè±¡ã§ããåããŒãä»¶ã§å€å®ãããšæ¯æ¥èª€å ±ã鳎ããæ¬ç©ã®å€±æãèŠåããããªããªããŸããtime-budgetãšããå¥åºå£ãçšæããŠåºå¥ããŸãã
諊ããå€å®åŒãcatchãããã¯ã«ãã¿æžããã
æã¡æ¢ãã®æ¡ä»¶ãcatchã®äžã«æžãããŠãããšãå¢çã±ãŒã¹ã®ãã¹ãã«èŒããããŸãããçŽç²é¢æ°ã«åºããšäºæ
åœæ¥ã®å®å€ã1ã±ãŒã¹å
¥ããããåãæ¡ä»¶ãäºåºŠãšéããªãããšãæ©æ¢°ã§ä¿èšŒã§ããŸããããžãã¯ãcatchã®äžã«ããéãããã¹ãã¯ãããŸããŸã±ãŒã¹ãéããªãã£ããç¶æ
ã§åæ ŒããŸãã
ãšã©ãŒã¡ãã»ãŒãžã«æ©æ¢°å¯èªãªæ å ±ãèŒã£ãŠããªã
ValidationErrorãæååã ãã§æãããšãåè©Šè¡æã«ãäœãæªãã£ããããã³ãŒãããåãåºããŸããã2åç®ä»¥éã«åãããã³ãããæãããšåãçµæã«ãªãã®ã¯ãæ»ãããå®è£
ãããŠããªãããã§ãcode: "title-too-long", actual: 63, limit: 58ã®ãããªæ§é ãèŒãããšä¿®æ£æç€ºãåçã«çµã¿ç«ãŠãããŸããLLMãžè¿ããšãã¯èå¥åïŒ"title-too-long"ïŒã§ãªã人éèªã®æç€ºæãšå®æž¬å€ãæž¡ããŸããèå¥åããã®ãŸãŸæž¡ããš3飿ããŸãã
ãã¹ããã©ã¯ãã£ã¹
5ã¬ãŒã³åæé害ããèžçããã«ãŒã«ãåæããŸããæ°ãå€ãã§ãããå šéšãå®éã«è©°ãŸã£ãããå ¥ããããã®ã§ãã
â 倱æã¯èšé²ããåã«HTTPã¹ããŒã¿ã¹ãšäŸå€ã®åã§åé¡ãã
429/503 â rate_limited / 401/403 â auth_error / ããä»¥å€ â åŠçç³»ãšã©ãŒããã®3åå²ãå
ã«çœ®ãã ãã§ãç»é¢ã«åºãã倱æããŠããåºã®äžèЧãã®æå³ãå€ãããŸããwatch-arbã¯ãã®åé¡ãå ããåŸãshops.last_errorã®æèšããã¬ãŒãå¶éã§ååŸã§ããããšãæ§é ãåããªããã«åããŸããã
â¡ ãªãã©ã€ãæžãããããã©ã«ãæ³šå ¥ãåãã³ãããã«å ¥ãã
let _failOnceRemaining = parseInt(process.env.WA_API_FAIL_ONCE ?? '0', 10);
// èµ·ååŸã®æåã®Nåã ãåæã®ãšã©ãŒãæãã
å®äºæ¡ä»¶ã¯2æ¬ã»ããã§ãã=2ã§retry attempt=1/3ãš2/3ãåºãŠexit 0ã§å®èµ°ããããšãæªèšå®ãªã retry ãã°ã1è¡ãåºãªãããšãâ¡ããªããšããã©ã«ã泚å
¥ãæ¬çªã«æŒããŠããªããããšãåãã³ãã³ãã§ç¢ºèªã§ããŸããã
⢠æã¡æ¢ãããåæ°ããšãåä»ããã®çµ¶å¯Ÿæå»ãã®äºéã§çœ®ã
åæ°ã ãã ãšã1åç®ã ãåéã12æéå
ããéããŸãã絶察æå»ã ãã ãšãåéã¯çããäžéãé£ç¶ããŠããããéããŸããkotonohaã®äºæ
ã¯æå»æ¡ä»¶ã®åŽã§1åç®ã«æãŸã圢ã§ããïŒåä»2026-08-19T00:36:57Zã»åé06:37:00Zãåä»+3æéã®å€ïŒãäºéã«ããæå³ãå®ããŒã¿ã§æž¬ããä»¶ã§ãã
â£ åŸ ã¡ã®åèšäžéãç§ã§å ã«æ±ºããåæ°ãéç®ãã
gsheets_retry.pyã¯ãåèšæå€§8ç§ã絶察ã«è¶
ããªãããäŸé Œæã«åºå®ããŠããã2ç§â6ç§ïŒ3åïŒãéç®ããŸãããææ°ããã¯ãªããåæ°ã§å
ã«æ±ºãããšãåŒã³åºãåŽã®ã¹ãããæéãéãã«äŸµé£ãããŸãã
†401/403/404ã¯å³throwããŠãªãã©ã€ã§é ããªã
if e.resp.status not in RETRYABLE_HTTP:
raise # æš©éãšã©ãŒã¯å³ raise
æš©éãšã©ãŒããªãã©ã€ã§å
ããšã3å倱æããããšã ãæ®ãããæš©éããªãããšããçå ãåãããŸããtransient-failure-recorded-as-permanent.mdã§èšé²ããã429ãextract_failedã«åããããšåãæ§é ã§ãä»åºŠã¯401ããããŸã«é
ããã«åããŸãã
⥠moderation_stageã§æ¢¯åãéãã䟡å€ããããå€å®ããŠããTierã詊ã
if result.get("moderation_stage") == "input":
log("inputæ®µã®æåŠãæ®ãTierãbreak")
break
1è¡å ããã ãã§63åã®ç©ºèµ°ãã0åã«ãªããŸããoutputã§æ¢ãŸã£ããã®ã¯ã©ããŒãåã䟡å€ããããŸããinputã§æ¢ãŸã£ããã®ã¯æŠå¿µããšæšãŠãŠæ¬¡ã«é²ã¿ãŸãã
⊠ãªãã©ã€ã®å±€ã1ç®æã«éçŽãã
åŒã³åºãåŽãç¬èªã«ãŒããæã¡ã©ã€ãã©ãªåŽã«ãå詊è¡ããããšææªNÃMåã«ãªããŸããéè€ãèŠã€ãããå€åŽã®ã«ãŒããåé€ããŠreq()ã®1å±€ã«äžæ¬åããŸãã
â§ éãå ã®å®¹éãæž¬ã£ãŠããé ç·ãã
ãã©ãŒã«ããã¯ã远å ããåã«ã宿ž¬ã§ããã®éãå ãäœä»¶ãŸã§è¿ããããã©ãã§è©°ãŸãããã確èªããŸãã容éã®ãªãéãå ã«å šãã©ãã£ãã¯ãæµããŠãæåçã¯å€ãããŸããã
âš æä¹ çãªå€±æã¯æ°žç¶ãªã¹ãã«1åã§èšé²ããŠé€å€ãã
no ã¡ãã»ãŒãž button / profile goneã®ãããªçžæåŽã®èšå®ã«èµ·å ãã倱æã¯ããªãã©ã€åæ°ãæ¶è²»ããåã«é€å€ããŸããshouldSkip()ã®å€å®ãexhaustedã®æåã«çœ®ããŸãã
â© ãä»ããäžåºŠå©ãããéããããå®éã«å©ããŠããäººéæ¡ä»¶ã«åé¡ãã
Cloudflare Authentication errorããOAuthã倱å¹ããããšå³æããŠ3æ¥é人éã®åŸ
ã¡è¡åã«ç©ãã§ããŸãããshell ãš launchd æå°ç°å¢ã®äž¡æ¹ã§ãããŒããå®èµ°ãããšå
šãŠexit 0ã§ããããšã©ãŒã®ååã ãã§ã絶察ã«çŽããªãããšåé¡ããªãã確ãããŠãã倿ããŸãã
⪠äžé宿°ãåœãã£ãããšèªäœããã°ã«åºã
text.slice(0, 400_000)ã¯ãµã€ã¬ã³ãã«åãèœãšãã ãã§ãããé²åŸ¡ã®å®æ°ãæä¹
çãªæ
éã®åŒãéã«ãªã£ãŠããŠãããã°ã«ã¯äœãæ®ããŸãããJSON.parseãŸã§å
éšã§å®äºããã倱æãããšãã ã蚺æçšã«å
é 300åãè¿ã圢ã«ãããšããåããããšããäºå®ãèšé²ã«æ®ããŸãã
â« ãªãã©ã€å°å ¥åŸã¯äŸå€ã®äŒæç¯å²ã®å€åã確èªãã
withRetryãå
¥ãããšãæ¡ãã€ã¶ãâthrowãã«å€ãã£ãç®æã®äžæµã«ããå¿
é åŠçãå°éäžèœã«ãªãããšããããŸãããªãã©ã€ã¯æ©èœã®è¿œå ã§ã¯ãªããäŸå€ã®äŒããæ¹ãå€ãã倿Žã§ããäžæµã«ã倱æããŠãå¿
ãããã¹ãããšããããç®æã¯åå¥ã«å²ã¿çŽããŸãã
â¬ è³æºéŒè¿«ãåå ã®å€±æã¯ãåŸ ã£ãŠããå詊è¡ãã
setContentSafelyã®å詊è¡ééããŒãç§ã§ã¯ãé«è² è·ã§èœã¡ãçŽåŸã«åæ¡ä»¶ã§ããäžåºŠèœã¡ãŸããæ±ºå®è«çãªå€±æã¯ã¹ãªãŒããªãå³å詊è¡ãæ£è§£ã§ããããã®éåŽãšããŠãè³æºãåŸ
ããªããšãªãã©ã€ã«æå³ããããŸããã倱æã®çš®é¡ã§ã¹ãªãŒããå
¥ããå Žæãéžã³ãŸãã
â 諊ããå€å®åŒãçŽç²é¢æ°ã«åºããŠãã¹ãã«äºæ åœæ¥ã®å®å€ãå ¥ãã
shouldGiveUpForQuota({quotaHits, createdAtMs, resumeAtMs})ãžåãåºããŠãåä»2026-08-19T00:36:57Zã»åé06:37:00Zã1ã±ãŒã¹ãšããŠå
¥ãããšãåãäºæ
ã¯äºåºŠãšéããŸãããcatchãããã¯ã«ãã¿æžããããå€å®ã¯å¢çã±ãŒã¹ããã¹ãã§ããŸããã
â® åŸ ã£ãäºå®ã»è«Šããäºå®ã»æã¡åã£ãäºå®ããã°ã®1è¡ã«æ®ã
waited=600s SKIP: slot timeoutãšSKIP: global limit reachedïŒå³æšãŠïŒã¯å¥ã®äºè±¡ã§ããã©ã¡ããåããSKIPãã«èŠãããšããæ ã®ç«¶åãå¢ããããªã®ããããšããšå³æšãŠããŠããããªã®ããåŸããåºå¥ã§ããŸãããæè³ããåŸ
ã¡æéã¯æ°å€ã§ãã°ã«åºããŸãã
ãŸãšã
ã倱æããããã1åããšæžãããšãããã®ã³ãŒãã«ã¯3ã€ã®åãã®çãããããŸãããåŸ ãŠã°çŽãããäœåã§èŠåãããæ¬¡ã®è©Šè¡ã«äœãå€ããŠæž¡ããããã®3ã€ãæã£ãŠããªãå®è£ ã¯ã決å®è«çãªå€±æã®åã§è³æºãçŒãç¶ããŠæ¢ãŸããŸãã
5ã¬ãŒã³ãåæã«æ¢ãŸã£ãæã«äžŠã¹ãŠæ°ã¥ããã®ã¯ãé害ã®åŸ©æ§ã«èªåãé¢äžããå¿ èŠããããã©ãããããã®èšèšã®æç¡ã§ã»ãŒæ±ºãŸããšããç¹ã§ããèšèšãããã°ãã¹ã¯ãªããã¯å€±æã®çš®é¡ããšã«æ¬¡ã®è¡åãèªåã§æ±ºããŸããèšèšããªããã°ãåã倱æã3åèšé²ãããã ãã§ãç¿æãåãå Žæã«è©°ãŸããŸãã
æå120äžã®èªååç°å¢ãæ¯æ¥åããŠããã®ã¯ãã³ãŒããæ¯æ¥æžããŠããããã§ã¯ãããŸããã倱æãæ£ããåé¡ãããŠãæ©æ¢°ãèªåã§å埩ããããæç«ããŠããŸãããã®åæã«ç«ã€ãšããªãã©ã€èšèšã¯ãåŸããå ããæ©èœãã§ã¯ãªããæåã®å®è£ ã«å«ãŸããŠããã¹ããã®ã§ãã
ããªãã©ã€ãæžãããã¯ãããã©ã«ãæ³šå ¥èŸŒã¿ã§èµ°ã£ããšãããèŠããã§å®äºã«ããŸãã
ä»çµã¿ã®å šäœåã»æ120äžã®å èš³ã»30æ¥æé ã¯æænoteã«ãŸãšããŠããŸãã
ð Claude CodeèªåŸç°å¢ã§ãå®éã©ã皌ãã â ä»çµã¿ã»å®äŸã»å§ãæ¹ã»ãµããŒã
LilyïŒ@bokuwalilyïŒâ å人éçºè ãClaude Code ã§èªåååºç€ãçµã¿ãªãããiOSã¢ããªãWebãµãŒãã¹ãéç£ããŠããŸã
- å¶äœç©ã»èšäºã¯ bokuwalily.com ã«ãŸãšããŠããŸãð¥ïž
- AIã§ãå¯ãŠãŠãåãä»çµã¿ããäœã£ãŠæ120äžã«ãã話㯠noteã®ææèšäº ã«ð°
- OSS: github.com/bokuwalily ð
- ææ°æ å ±ã»ãåãåãã㯠X @bokuwalily ãžð
- AIå°å ¥ã»èªååã®çžè«ãšå®è£ ãã³ãã¬7æ¬ã®é åžã¯ å ¬åŒLINE ããð¬
çããã® â€ïž ãã·ã§ã¢ãå±ã¿ã«ãªããŸãïŒ