Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

[plugin-dashboard] a table widget with inline static data crashes into the error boundary — "Maximum update depth exceeded" #4618

Closed

Description

@yinlianghui

Measured while implementing (d1) of #4600 (PR #4615), probing which widget data surfaces render in the docs gallery. Filed unassigned; not fixed there — packages/plugin-dashboard/src/** was outside that card's scope. Duplicate-searched (keyword + data-table path): no open issue covers it.

Reproduction, measured on origin/main @ c1d939f7f

Rendered through the real SchemaRenderer, with @object-ui/components + @object-ui/plugin-dashboard + @object-ui/plugin-charts registered:

{
  "type": "dashboard", "columns": 2, "gap": 4,
  "widgets": [
    { "id": "t", "title": "Recent Invoices", "type": "table",
      "options": { "data": [ { "name": "INV-1", "amount": 100 },
                             { "name": "INV-2", "amount": 200 } ] } }
  ]
}

Rendered output (textContent, one role="alert"):

Recent InvoicesComponent "data-table" failed to render
Maximum update depth exceeded. This can happen when a component repeatedly calls
setState inside componentWillUpdate or componentDidUpdate. React limits the
number of nested updates to prevent infinite loops.
Retry

The same probe rendered bar, line, area, donut and metric widgets from the identical static surface with no alert and a mounted chart container, so the defect is specific to the table family (dispatch.family === 'table'), which DashboardRenderer.getComponentSchema maps to:

return { type: 'data-table', ...options, data: Array.isArray(widgetData) ? widgetData : widgetData?.items || [],
         searchable: false, pagination: false, className: "border-0" };

Not established here: whether the loop lives in data-table itself or in what the dashboard spreads onto it (note ...options spreads the author's options, including the data key, before data is set — so data is passed twice from two derivations). A one-line reproduction against data-table directly would settle that, and is the obvious first step.

Why it matters

table is a spec-enumerated dashboard widget type and the static options.data surface is live and documented (content/docs/guide/dashboard-filters.md describes static-data widgets as a supported, unfiltered surface). No catalog entry exercises it, which is why nothing caught this — PR #4615 chose chart/metric surfaces for the entries it rewrote and never landed on table.

Adjacent, not the same: this is the STATIC path. The provider: 'object' path routes to object-data-table instead and is untested here.

Refs #4600, PR #4615.


Generated by Claude Code

Activity

  1. self-assigned this
    on Aug 14, 2026
  2. yinlianghui commented on Aug 14, 2026

    @yinlianghui
    ContributorAuthor

    CLAIM — session session_017Qqyix2QcnpUC9XeYVDzx3, branch claude/issue-4618-datatable-update-depth.

    PM ruling (delegated, veto window open): diagnose-first, then the minimal correct fix. The filing's lead ("DashboardRenderer passes data twice — via ...options and the derived key") is a HYPOTHESIS to verify, not the ruling: reproduce the crash red-first exactly as filed (static-data table widget → error boundary "Maximum update depth exceeded" at packages/components/src/renderers/complex/data-table.tsx:788), trace the actual re-render loop (the usual shapes: an unstable prop identity feeding a useEffect/useState sync, or a controlled/uncontrolled flip), and fix at the CAUSE — in whichever package owns it (components' data-table renderer or plugin-dashboard's node construction; both surfaces are currently free). Must-not-change: every other data-table consumer (grid/list/detail radius), and the dashboard's other widget families. If the fix lands in components, sweep data-table's full consumer radius; if in plugin-dashboard, mirror-check DashboardGridLayout's equivalent construction (post-#4613/#4614 both build these nodes). Grading by measurement; behavior fix ⇒ patch or minor per the established precedents; never major. Standard playbook: worktree, red-first verbatim, reverse verification (no git stash), repo-root vitest, .d.ts both ways, gate battery, DRAFT PR (Fixes #4618), CI via MCP only, no flip/arm.

    Mutual exclusion: no other seat in flight; ⛔ content/docs/releases/**; ⛔ the #4616 seat (dispatching in parallel) owns apps/site/** + examples/schema-catalog/test/** — if your fix needs a catalog-side pin, coordinate by leaving it to #4616's render-pin extension and say so.


    Generated by Claude Code


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions