perspective-dev/perspective · error

facet-grid: cell size ( × ) differs from cell 0 ( × )

Error message

facet-grid: cell ${i} size (${r.width}×${r.height}) differs from cell 0 (${r0.width}×${r0.height})

What it means

Internal invariant check in facet grid layout: after computing the grid, every facet cell must share the same plot rect dimensions as cell 0, because tick sampling and projection math assume uniform cell sizes. The warning fires when a computed cell's width or height diverges, indicating a layout or constraint bug rather than a user-input error.

Solutions

  1. Inspect facet configuration (facet_padding, legend mode) for values that round differently per cell
  2. Check for fractional pixel rounding differences caused by non-integer container sizes
  3. File a bug with the facet config and container dimensions, as this signals a renderer layout defect
  4. As a workaround, set explicit uniform chart dimensions so all cells compute identical rects
Defensive patterns

Strategy: fallback

When it happens

Trigger: Thrown at packages/viewer-charts/src/ts/charts/cartesian/cartesian-render.ts:634 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of perspective-dev/perspective@11c8238c0c (2026-09-09). Data as JSON: /api/errors/79a20f8487e79915. Report an issue: GitHub.

Appendix: source

Thrown at packages/viewer-charts/src/ts/charts/cartesian/cartesian-render.ts:634

            resolveLegendMode(chart._pluginConfig, legendEntryCount(chart)) ===
                "sidebar",
        legendWidth: legendSidebarWidth(chart._pluginConfig, 96),
        hasXLabel: !bareMap && !!chart._xLabel,
        hasYLabel: !bareMap && !!chart._yLabel,
        gap: chart._facetConfig.facet_padding,
    });
    chart._facetGrid = grid;

    // Grid invariant: every cell has the same plot rect dimensions.
    // Downstream code (tick sampling, projection math) depends on
    // this. The O(N) comparison runs at most once per frame and bails
    // at the first mismatch — cheap enough to leave on unconditionally.
    if (grid.cells.length > 1) {
        const r0 = grid.cells[0].layout.plotRect;
        for (let i = 1; i < grid.cells.length; i++) {
            const r = grid.cells[i].layout.plotRect;
            if (r.width !== r0.width || r.height !== r0.height) {
                console.warn(
                    `facet-grid: cell ${i} size (${r.width}×${r.height}) ` +
                        `differs from cell 0 (${r0.width}×${r0.height})`,
                );
                break;
            }
        }
    }

    // `_lastLayout` backs the hover hit-test in `continuous-interact.ts`.
    // In faceted mode the hover routine resolves the facet under the
    // cursor and consults that cell's layout directly; for legacy
    // fallback (shouldn't fire), publish the first cell's layout.
    chart._lastLayout = grid.cells[0]?.layout ?? null;

    // Keep every controller's layout pointer fresh for wheel/pan math.
    chart.syncFacetZoomLayouts(grid.cells);
    const independent = chart._facetConfig.zoom_mode === "independent";

View on GitHub (pinned to 11c8238c0c)