{"record":{"id":"fb45d30ef2c1ab27","repo":"pbakaus/impeccable","slug":"impeccable-svelte-component-abort-cleanup-failed","errorCode":null,"errorMessage":"[impeccable] Svelte component abort cleanup failed:","messagePattern":"\\[impeccable\\] Svelte component abort cleanup failed:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"skill/scripts/live-browser.js","lineNumber":6109,"sourceCode":"    if (state !== 'CYCLING') setLiveState('GENERATING');\n    injectSvelteComponentsFromManifest(manifestPath, sessionId);\n  }\n\n  // Tear down a component preview that could not mount, WITHOUT touching\n  // session identity. The old version cleared localStorage, nulled\n  // currentSessionId, and reset to PICKING, which orphaned a session the server\n  // still had in its journal and made every recovery path unreachable. The DOM\n  // teardown and observer cleanup are still right; the state wipe never was.\n  function abortSvelteComponentInjection(sessionId, details) {\n    try {\n      if (svelteComponentSession?.sessionId === sessionId) {\n        teardownSvelteComponentSession(true);\n      } else {\n        const orphan = document.querySelector('[data-impeccable-variants=\"' + sessionId + '\"]');\n        if (orphan) orphan.remove();\n      }\n    } catch (err) {\n      console.warn('[impeccable] Svelte component abort cleanup failed:', err);\n    }\n    hideShaderOverlay();\n    if (pendingSvelteComponentRetryObserver) { pendingSvelteComponentRetryObserver.disconnect(); pendingSvelteComponentRetryObserver = null; }\n    if (pendingVariantAnchorRetryObserver) { pendingVariantAnchorRetryObserver.disconnect(); pendingVariantAnchorRetryObserver = null; }\n    // The generate submit armed a scroll lock and a variant observer; a page\n    // the user cannot scroll, watched by a stale observer, is exactly the\n    // wrong place to show a card asking them to act.\n    stopScrollLock();\n    if (variantObserver) { variantObserver.disconnect(); variantObserver = null; }\n    removeVariantStateStylesheet();\n    hideBar(true);\n    // currentSessionId, the saved session, and the file metadata all survive on\n    // purpose: Retry, a republish from the agent, and a page reload all need\n    // them. saveSession keeps the localStorage cache in step with the server.\n    saveSession();\n    if (details) showMountErrorCard(sessionId, details);\n    else if (!mountErrorState) {\n      showMountErrorCard(sessionId, { message: 'Variants could not be mounted. Retry, or ask the agent to republish.' });","sourceCodeStart":6091,"sourceCodeEnd":6127,"githubUrl":"https://github.com/pbakaus/impeccable/blob/f88b2837a7d7c3182e46307bbbb091a1ed547571/skill/scripts/live-browser.js#L6091-L6127","documentation":"abortSvelteComponentInjection runs when a svelte-component generate/steer is aborted: it tears down the matching session (or removes an orphaned [data-impeccable-variants] container). That teardown is wrapped in try/catch; if the DOM work throws — typically because the page's own framework already removed or remounted the nodes — the error is logged as 'abort cleanup failed:' and the remaining cleanup (observers, scroll lock, overlay, bar) still executes.","triggerScenarios":"The inspected app rerenders/remounts the variant region between abort decision and teardown; abort fires twice so the second pass finds already-detached nodes; teardownSvelteComponentSession walks a stale snapshot of removed elements.","commonSituations":"SPA hot-reload or hydration racing the abort; user navigating the app while aborting a generation; double-click on an abort control.","solutions":["Usually benign — verify no variant remnants, overlay, or scroll lock remain; reload the page if any do","Guard teardown paths with element.isConnected checks before acting","Debounce/dedupe abort triggers so cleanup runs once"],"exampleFix":"// before\nconst orphan = document.querySelector('[data-impeccable-variants=\"' + sessionId + '\"]');\nif (orphan) orphan.remove();\n// after\nconst orphan = document.querySelector('[data-impeccable-variants=\"' + sessionId + '\"]');\nif (orphan?.isConnected) orphan.remove();","handlingStrategy":"fallback","validationCode":"// Only tear down DOM that is still in the document\nconst orphan = document.querySelector(`[data-impeccable-variants=\"${sessionId}\"]`);\nif (orphan && !orphan.isConnected) return; // nothing live to clean","typeGuard":null,"tryCatchPattern":"try { teardownSvelteComponentSession(true); } catch (err) { console.warn('abort cleanup failed', err); } // always continue with observer/scroll-lock cleanup","preventionTips":["Dedupe abort triggers so teardown runs once per session","Prefer re-querying DOM at cleanup time over cached element references","After aborts, spot-check for leftover overlays/scroll lock and reload if needed"],"tags":["dom-cleanup","svelte-preview","abort","race-condition"],"backgroundTag":"dom-cleanup-race-condition","analyzedSha":"f88b2837a7d7c3182e46307bbbb091a1ed547571","analyzedAt":"2026-08-18T04:58:36.608Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}