Skip to content

RangeError: Maximum call stack size exceeded in recomputeDerivedRows once one project exceeds ~125k usage records[Bug]: #126

Closed

Description

@mkgaskin-ops

Preflight checklist

  • I searched existing issues and didn't find a duplicate.
  • I'm on the latest version of the extension, or I've written my version below.
  • I read the README's Troubleshooting section (proxies, history retention, the AI advice feature).

What happened?

Version 2.3.2, VS Code on Windows 11, Node/V8 in the extension host.

Two sites build a row with a spread over every usage record's timestamp:

  1. claudeIncrementalIndex.js lines 902-903 (compiled output), the project row:

    firstSeen: timestamps.length > 0 ? new Date(Math.min(...timestamps)) : new Date(0),
    lastSeen: timestamps.length > 0 ? new Date(Math.max(...timestamps)) : new Date(0),

  2. dataLoader.js line 2689, the branch row's lastSeen, the same pattern.

timestamps has one element per usage record, so the spread pushes one argument per record onto the call stack. Past roughly 125,000 records V8 throws RangeError: Maximum call stack size exceeded.

recomputeDerivedRows is called from updateClaudeUsageIndex OUTSIDE that function's own try/catch, so the throw escapes to the bare catch {} in extension.js runRefresh. The user sees a permanently loading panel and a log line with failed=1, where every field except total is a hard-coded constant in the error path, so the log implies a scan failure when the scan succeeded.

Reproduction on my corpus: one project group past ~124,500 records; the measured spread ceiling on this machine's Node is ~124,900. Running the same updateClaudeUsageIndex in a worker_thread with resourceLimits.stackSizeMb = 256 completes normally with filesFailed: 0. The only variable is stack size. Because the refresh retries on a 60 s poll plus focus and watch triggers, and each attempt re-reads the whole corpus before throwing, the extension host sits in a near-continuous full read loop once a user crosses the threshold.

Suggested fix: replace both spreads with a loop, e.g.

let lo = Infinity, hi = -Infinity;
for (const t of timestamps) { if (t < lo) lo = t; if (t > hi) hi = t; }

Secondary suggestion: log the caught error in runRefresh. The bare catch made this take a source read and a standalone repro to find.

I am running a local patch of exactly this change on 2.3.2 and the panel refreshes normally (every refresh failed=0).

What did you expect instead?

The usage panel should finish its refresh and show the usage data. Instead it stays on "loading" forever once one project passes ~125k usage records, because the timestamp spread throws a RangeError that a bare catch hides. Expected: the refresh completes (as it does with the two spreads replaced by a loop), and any refresh error is logged instead of swallowed.

Steps to reproduce

No response

Extension version

2.3.2

Claude Code version

2.1.291

VS Code version

1.140.0

Operating system

Windows

Model / proxy setup

VS Code extension 2.1.289

Diagnostic logs


Anything else?

No response

Activity

  1. github-actions commented on Oct 6, 2026

    @github-actions

    🤖 Automated first-pass reply

    **TL;DR**

    This is a real stack overflow in aggregate-row derivation: Math.min(...timestamps) / Math.max(...timestamps) pass one argument per usage record. Above roughly 125,000 records, V8 throws RangeError: Maximum call stack size exceeded. Replace both timestamp-envelope spreads with a loop, and make runRefresh log the caught error instead of swallowing it.

    Analysis

    • The bug matches src/claudeIncrementalIndex.ts ownership: it materializes dashboard aggregates and owns the projectRows update path. The reported recomputeDerivedRows belongs to that materialization path.
    • src/dataLoader.ts owns the equivalent branch-row path, where the second spread is reported.
    • Because updateClaudeUsageIndex throws outside its own try/catch, the bare catch {} in extension.ts hides the failure. The issue’s description of a permanently loading panel and misleading “scan failed” diagnostic is consistent with that.
    • The suggested loop avoids argument-stack limits without changing aggregation semantics.

    Suggested next step(s)

    1. In src/claudeIncrementalIndex.ts, replace the project-row Math.min/max(...timestamps) envelope with a single loop that tracks lo and hi.
    2. In src/dataLoader.ts, replace the branch-row lastSeen spread with the same loop pattern, or use a shared helper if that does not blur module ownership.
    3. Add a focused regression test in src/test/ with an array large enough to overflow the old spread path; the issue reports the local ceiling around 124,900, so use that as the threshold reference.
    4. In src/extension.ts runRefresh, log the caught error in redacted form rather than swallowing it, so future refresh failures are diagnosable without a source read and standalone repro.
    5. Confirm the current src/ line numbers before patching: the issue cites compiled out/ lines, and the supplied snippets do not include those exact sections.

    For a behavior change, include a CHANGELOG.md entry and update the relevant tests as required.


    🤖 Generated by DeepSeek as an automated first pass — not a maintainer decision.

  2. Carl723000 commented on Oct 15, 2026

    @Carl723000
    Collaborator

    Thanks @mkgaskin-ops for the precise reproduction. The specific argument-stack overflow is fixed in 2.5.0, now publicly available on Marketplace and Open VSX, through #131.

    The project/session/branch/workflow timestamp bounds no longer pass an unbounded record array as function arguments. Regression coverage includes 150,000 records in one project, exact time bounds/totals, and an unchanged refresh with zero body reads. Those four large-history tests were rerun successfully after release; the release-head Node/UI/package gates also passed. Refresh failure handling retains verified results and records a bounded failure category rather than inventing a file-scan failure or logging arbitrary error text.

    Closing this specific released stack-overflow defect. Please reopen if the same failure persists with the installed extension at 2.5.0, with anonymous refresh diagnostics and the affected grouping/approximate record count. No conversation logs, credentials or personal paths are needed. This is not a claim that all CPU, memory or Windows renderer issues are resolved; #99 and #122 remain separate.


    🤖 Generated by OpenAI Codex as an automated first pass — not a maintainer decision.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions