fix(items): retry failed model loads, settle skipped items, keep exports clean#480
Conversation
…rts clean A transient storage failure (observed: Supabase 504s under the bake page's ~200-concurrent-request burst) permanently broke an item for the whole session: drei's useGLTF caches the rejected load by URL, the per-item ErrorBoundary swallowed it, and the red debug-box fallback was BAKED into the exported GLB (observed in a prod artifact). Meanwhile ItemSystem cleared the dirty mark at group registration — before the model resolved — so scene-ready could fire while GLBs were still loading, risking exported placeholder geometry. - ModelWithRetry: bounded retries (1s/3s) that clear the useGLTF cache entry and re-mount via the boundary's new resetKey. The timer is owned by an effect keyed on the failure count, so StrictMode's synthetic unmount/remount re-arms it instead of silently discarding it (the naive onError-scheduled timer died exactly that way in dev). - Exhausted retries settle the item as SKIPPED: the debug box renders nothing during exports, and the failure lands in useViewer.itemLoadFailures (nodeId -> url) so a bake host can persist which items are missing from the artifact. - ItemSystem holds the dirty mark until the item settles (model mounted, terminally failed, or never expected) — scene-ready now genuinely waits for item content; loading placeholders also hide during exports. - ErrorBoundary: onError + resetKey props. Verified against a 200+-item prod scene locally: permanent 504 -> exactly 3 fetch attempts, bake completes without the item and without debug boxes; 504-once -> retry heals, artifact byte-equivalent to the intact run; no-failure runs unchanged (demo_1 byte-identical). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 6a7d9e9. Configure here.
| // isn't "built": clearing then would let scene-ready fire while GLBs are | ||
| // still downloading and a bake would export loading placeholders. | ||
| const settled = (mesh.userData as { itemModelSettled?: boolean }).itemModelSettled | ||
| if (!settled) return |
There was a problem hiding this comment.
Scene ready beats item settlement
High Severity
ItemSystem now keeps items in dirtyNodes until itemModelSettled, but SceneReadyTracker still marks the scene ready after SCENE_READY_MAX_WAIT_FRAMES even when hasPendingSceneBuildWork() remains true. A bake that follows that signal can export while GLBs are still loading; with isExporting, item placeholders render nothing, so those items can appear as empty groups in the GLB.
Reviewed by Cursor Bugbot for commit 6a7d9e9. Configure here.
Bugbot: after a terminal load failure, swapping the item's model kept the stale failures/epoch state and the settled flag — the new URL never even attempted to load. ModelWithRetry is now keyed by asset src (clean retry budget per URL) and un-settles the item on mount so the replacement load is awaited by ItemSystem/scene-ready too. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Addressed both Bugbot findings:
|


What does this PR do?
Makes item model loading resilient for both interactive use and headless bakes. Today a transient storage failure (observed in prod: Supabase 504s under the bake page's ~200-concurrent-request asset burst) permanently breaks an item for the session — drei's
useGLTFcaches the rejection — and worse, the red debug-box fallback gets baked into the exported GLB (observed in a prod artifact: 24-vert boxes with the red translucent material where 4 kettles should be).useGLTFcache entry and re-mount via a newresetKeyonErrorBoundary. The retry timer is effect-owned, keyed on failure count — StrictMode's synthetic unmount/remount re-arms it (an onError-scheduled timer dies silently in dev, verified).isExporting, and the failure is recorded inuseViewer.itemLoadFailures(nodeId → url) so a bake host can persist which items are missing.ItemSystemnow holds the dirty mark until the item settles (model mounted / terminally failed / never expected) instead of clearing at group registration — closing a race where a bake could export loading placeholders; the loading placeholder also hides during exports.How to test
bun dev, open a scene with items — loading placeholders, hover/selection, and slot materials behave as before.Verified against a 200+-item prod scene: permanent 504 → exactly 3 fetch attempts then a clean skip; 504-once → retry heals, artifact byte-equivalent to intact; no-failure runs byte-identical (demo_1).
Screenshots / screen recording
N/A — failure-path behavior; exported-GLB assertions above are the evidence.
Checklist
bun devbun checkto verify)mainbranch🤖 Generated with Claude Code
Note
Medium Risk
Changes item load lifecycle and scene-ready gating used by headless GLB bakes; incorrect settlement timing could delay exports or omit items, but happy-path behavior is intended to stay byte-identical.
Overview
Item GLB loading is now resilient to transient CDN/storage failures and headless bakes no longer capture debug or loading placeholders.
ModelWithRetrywraps item loads with bounded retries (1s / 3s): on failure it clears drei'suseGLTFcache, bumps anErrorBoundaryresetKey, and re-mounts. After retries are exhausted the item settles as skipped—the red debug box stays in dev but renders nothing whenisExporting; failures are recorded inuseViewer.itemLoadFailures(nodeId → URL) for bake metadata.Scene-ready / bake timing:
ItemRenderersetsuserData.itemModelSettledwhen the model mounts, fails terminally, or isn't needed (e.g. room-clear preview).ItemSystemonly clears the dirty mark once settled, so scene-ready and exports wait for real geometry instead of grey loading boxes.Export hygiene:
PreviewModelandBrokenItemFallbackreturnnullduring export.ErrorBoundarygainsonErrorandresetKeyto support the retry flow.Reviewed by Cursor Bugbot for commit 2b032cc. Bugbot is set up for automated code reviews on this repo. Configure here.