Skip to content

RangeError: Maximum call stack size exceeded in Cache.add() for large raw-state histories (~>60k points) #642

Description

@homuell

Card version: v3.3.5 (commit 20e9686)

Summary

Cache.add() merges freshly fetched history into the cumulative cache using a spread call:

// src/cache/Cache.ts, add()
h.push(...states);

When states has tens of thousands of elements (a raw, non-statistics entity with a high
state-change rate, viewed over a multi-day range), this spread throws
RangeError: Maximum call stack size exceeded — this is the JS engine's argument-count limit
for spread/apply-style calls, not "real" recursion, but the resulting error is identical.

How it surfaces to users

Because ConfigParser.evalNode() wraps each per-entity property evaluation in its own
try/catch (parse-config.ts), the crash doesn't abort the whole card — it gets caught and
re-thrown, once independently, for every $fn-valued default property of the affected
entity that triggers a fetch (on_legend_click, on_legend_dblclick, on_click,
unit_of_measurement, hovertemplate, yaxis, or line.color if the entity has no explicit
color). Each of these properties independently calls fetchDataForEntity() again (since the
throw happens before this.fnParam.xs is ever set), so the same crash — and a full history
re-fetch — happens repeatedly per render attempt. The end-user symptom is a card-level error
block listing several unrelated-looking config paths, e.g.:

Error: at [entities.6.on_legend_click / on_legend_dblclick / on_click /
unit_of_measurement / hovertemplate / yaxis]: Maximum call stack size exceeded
Error: at [entities.7.line.color]: Maximum call stack size exceeded

Reproduction / evidence

Card config: 19-trace stacked area chart (fill: tonexty/tozeroy) over several distinct
Home Assistant power sensors (state_class: measurement, no statistic/period set — i.e.
raw states, ~10s update cadence from the underlying source). Rangeselector buttons up to 14
days. Counting raw states rows per sensor over trailing windows (from the HA recorder DB):

entity 3d 7d 14d
sensor.pv_bilanz_verb_c2 72,883 169,991 240,056
sensor.pv_hauslast_negativ 73,286 171,009 242,699
sensor.pv_bilanz_prod_c2 53,574 127,134 179,827
sensor.pv_echte_leistung_gesamt 43,221 103,068 181,691
sensor.pv_bilanz_verb_c1 46,959 110,714 156,441
(6 more sensors on the same card) 8k–21k 20k–49k 36k–77k

The crash was first observed (in production) at the "3d" button, on the two entities with the
highest point counts at that window (72,883 and 73,286) — consistent with a hard argument-count
ceiling somewhere in the 65k-ish range (varies by JS engine/platform; mobile WebKit is known to
have a lower ceiling than desktop V8). At 7d/14d essentially all raw-state traces on this card
are past the danger zone.

Expected behavior

Large raw-state histories should merge into the cache without hitting an engine argument limit.

Suggested fix

Replace the spread with an iterative push (functionally identical, no behavior change, avoids
building a giant argument list):

- h.push(...states);
+ for (const s of states) h.push(s);

(equivalently states.forEach((s) => h.push(s))). Happy to open a PR if useful — this is a
one-line change with no test coverage implications I can see, but I don't know your preferred
contribution process.

Workaround considered and rejected on our end

We considered switching the affected entities to statistic/period (long-term statistics)
instead of raw states, which sidesteps the point-count problem entirely. We didn't do this
because (a) it permanently reduces resolution even for short (1h/6h) ranges where raw data
would render fine, and (b) our traces rely on exact per-point min/max relationships between
several derived sensors to guarantee correct stacking (fill: tonexty), which isn't preserved
by independently-averaged 5-minute means. We'd rather wait for/contribute the real fix than
carry that trade-off.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions