fix(settings): cover slow SessionEnd hooks with CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS#1308
Conversation
…over slow SessionEnd hooks Claude Code v2.1.74+ caps the entire SessionEnd hook chain at 1.5 seconds by default (documented at https://code.claude.com/docs/en/hooks). The v5.0.0 SessionEnd chain includes UpdateCounts.hook.ts, which makes two synchronous Anthropic API calls inside refreshUsageCache (usage API with 3s timeout + cost_report API with 5s timeout, worst case ~8s). UpdateCounts alone blows the 1.5s budget by 2-5x, producing recurring terminal output: SessionEnd hook $HOME/.claude/hooks/UpdateCounts.hook.ts failed: Hook cancelled When the budget expires, subsequent hooks (IntegrityCheck, KVSync) never start — statusline counts and Cloudflare KV sync go stale. Setting CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=10000 (10 seconds) gives the chain enough budget to complete UpdateCounts plus its neighbors (IntegrityCheck 0.6s + KVSync 0.9s measured locally) with headroom. Related: danielmiessler#965
|
Hey @jacobo-ortiz, thanks for raising this, and sorry it sat for a while. We're changing how LifeOS ships. Instead of cloning a full That's aimed right at what you hit here. The old "one directory, one layout, hope it matches your setup" approach is exactly what broke for so many people, and the new model should handle it far better because your AI does the integration per machine instead of us guessing. So we're closing this in prep for that release. If it still bites you once the skill-based version is out, reopen or file a fresh one and we'll jump on it. Appreciate you taking the time. |
Problem
Claude Code v2.1.74+ caps the entire
SessionEndhook chain at 1.5 seconds by default (Claude Code docs; originally surfaced via anthropics/claude-code#33706). The per-hooktimeoutfield (default 600s) is bounded by this chain-wide budget, not in addition to it.PAI v5.0.0 ships
UpdateCounts.hook.tsinSessionEndposition 4. Its handler (hooks/handlers/UpdateCounts.ts) makes two synchronous Anthropic API calls insiderefreshUsageCache:usageOAuth API —AbortSignal.timeout(3000)at line 214cost_reportadmin API —AbortSignal.timeout(5000)at line 234 (only whenANTHROPIC_ADMIN_API_KEYis set)Worst case: ~8 seconds. Measured locally on macOS, 5 runs: 3.05 / 3.72 / 7.42 / 6.46 / 3.74 seconds. UpdateCounts alone blows the 1.5s
SessionEndbudget by 2–5×, producing the recurring output:When the chain budget expires mid-execution, subsequent hooks (
IntegrityCheck,KVSync) never start — statusline counts go stale and Cloudflare KV sync is skipped.Why this is structural, not a bug in UpdateCounts
UpdateCounts.hook.tsandhandlers/UpdateCounts.tsare byte-identical to upstream v5.0.0 — confirmed viadiff./usr/bin/time -p× 5) shows 3.05 to 7.42 s — the API calls are the bottleneck regardless of session state.IntegrityCheck0.60 s,KVSync0.91 s.The fix
Add
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=10000to theenvblock inReleases/v5.0.0/.claude/settings.json. 10000 ms gives UpdateCounts worst-case (~8 s) headroom while keeping the chain bounded."env": { "PAI_DIR": "${HOME}/.claude/PAI", "PROJECTS_DIR": "${HOME}/Projects", "BASH_DEFAULT_TIMEOUT_MS": "600000", "API_TIMEOUT_MS": "1800000", + "CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS": "10000", "PAI_CONFIG_DIR": "${HOME}/.claude/PAI", "GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE": ... },Verification
jq '.env.CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS' settings.json→"10000"jq empty settings.json→ JSON valid (no parse errors)Hook cancelledline for UpdateCounts / IntegrityCheck / KVSync.Related
SessionEndtimeout override behavior anthropics/claude-code#33706 — docs issue that named the 1.5 s default; closed RESOLVED 2026-03-18.Notes for maintainers
UpdateCountsworst case with margin; 5000 ms would leave thecost_reportAPI path at risk on slow networks.refreshUsageCacheto a detached subprocess (returning the hook in <50 ms) would eliminate the budget pressure entirely, but it's a larger change. The env-var addition is the minimal fix that resolves the symptom for every PAI user today.