Repository navigation
fix(host-service): stop usage.history throwing on very large usage histories - #6912
Conversation
…stories collectUsageEntries appended the deduped Claude entries with entries.push(...map.values()), which passes every entry as a call argument. Past V8's argument limit (~125k entries in the worker) that throws RangeError: Maximum call stack size exceeded, so usage.history failed deterministically for exactly the heaviest users. Append in a loop instead — stack usage no longer scales with entry count. Refs HOST-SERVICE-4V Claude-Session: https://claude.ai/code/session_0133xN5zZ3goUNfKAy9cLfA9
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughThe usage history collector now appends deduplicated Claude entries individually. This avoids the V8 argument-count limit for large histories without changing other behavior. ChangesUsage history
Estimated code review effort: 1 (Trivial) | ~5 minutes Merge Risk: ⚪ Minimal · up to The change prevents very large usage histories from failing while preserving existing behavior and outputs. No actionable merge-blocking risk remains after normal checks and review. Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Description checkExplanation The description explains the problem, root cause, fix, scope, and verification results. It provides the required testing information under “Verification,” but it does not include the template’s Checklist section.
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🧹 Preview Cleanup CompleteThe following preview resources have been cleaned up:
Thank you for your contribution! 🎉 |
Problem
usage.history(and anything else routed throughcollectUsageEntries) fails deterministically on machines with a very large Claude usage history: the usage worker throwsRangeError: Maximum call stack size exceeded, so the Usage tab's history and leaderboard data never load — every time, for exactly the heaviest users. 3 events on release 1.25.0 (HOST-SERVICE-4V), all the same machines retrying.User-visible harm: heavy Claude users open the Usage tab and its history/leaderboard never loads, on every attempt.
Reach: the defect shipped in the leaderboard work (#6875, release 1.25.0) and is unchanged on main. This fix rides the next host-service/desktop release and reaches affected users via the normal update path; the failure is deterministic per machine, so the fix verifiably removes it for them.
Why code: archiving does nothing — the feature is broken for those users, and the failure recurs on every query. A Sentry-side filter is forbidden by the classification contract (#6164) and wouldn't restore the feature. The one-line root-cause fix wins.
Root cause
packages/host-service/src/trpc/router/usage/history/entries.ts:83:Argument spread passes every deduped Claude entry as a distinct call argument. V8 bounds call arguments by stack size, so past roughly 100–125k entries the call itself throws
RangeError: Maximum call stack size exceeded. A heavy user's 30-day window can exceed that (one entry per assistant API message). Reproduced under Node 22 (the worker's engine) with a 200k-entry map:(Bun/JSC tolerates 200k, which is why local
bun testnever caught it — production host-worker runs on V8.)Fix
Append in a loop — stack usage no longer scales with entry count:
Worker-boundary payload does NOT need bounding: I verified what actually crosses the MessagePort.
computeUsageHistoryreturnsUsageHistory, already a bounded aggregate — day buckets bounded by thedaysparam, per-model rows bounded by distinct models,projectDetailscapped at top 24 projects with top 20 sessions each (aggregate.ts).computeLeaderboardPayloadreturns day×provider×model buckets, likewise bounded. The unboundedentriesarray is internal to the worker and never serialized, so the spread is the whole defect and this is the minimal fix.No typed cause added: this fix removes a throw rather than adding one — the query now succeeds. Nothing reads a
cause.kindhere, so noTRPCErrormachinery was introduced.Adjacent, deliberately untouched:
parseCodexLogFilepushes entries one at a time already (no spread); the array-literal spreads inaggregate.ts([...map.values()]) iterate rather than pass call arguments and have no argument-count limit; the desktop minidump OOM issues (DESKTOP-12V et al.) are a separate memory-scaling face of this subsystem owned by the triage automation and are not addressed here.Verification
Refs HOST-SERVICE-4V
https://claude.ai/code/session_0133xN5zZ3goUNfKAy9cLfA9
Summary by cubic
Fixes
usage.historyfailing withRangeError: Maximum call stack size exceededfor users with very large Claude usage histories, so the Usage tab history and leaderboard now load for heavy users. The deduped entries are appended one at a time instead of spread intopush(), which passed every entry as a call argument and hit V8's argument limit. No payload changes are needed since the worker-boundary data is already bounded. Refs HOST-SERVICE-4V.Written for commit b314cf1. Summary will update on new commits.
Summary by CodeRabbit