{"record":{"id":"d484ffa9e3bf5b82","repo":"TanStack/query","slug":"devtools-is-not-mounted-d484ff","errorCode":null,"errorMessage":"Devtools is not mounted","messagePattern":"Devtools is not mounted","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/query-devtools/src/TanstackQueryDevtoolsPanel.tsx","lineNumber":165,"sourceCode":"              return hideDisabledQueries()\n            },\n            get onClose() {\n              return onClose()\n            },\n            get theme() {\n              return theme()\n            },\n          }}\n        />\n      )\n    }, el)\n    this.#isMounted = true\n    this.#dispose = dispose\n  }\n\n  unmount() {\n    if (!this.#isMounted) {\n      throw new Error('Devtools is not mounted')\n    }\n    this.#dispose?.()\n    this.#isMounted = false\n  }\n}\n\nexport { TanstackQueryDevtoolsPanel }\n","sourceCodeStart":147,"sourceCodeEnd":173,"githubUrl":"https://github.com/TanStack/query/blob/159982c80b844487054155c38ffc760fe4208daf/packages/query-devtools/src/TanstackQueryDevtoolsPanel.tsx#L147-L173","documentation":"Thrown by `TanstackQueryDevtoolsPanel.unmount()` when `unmount` is called on an instance with `#isMounted === false`. Mirrors the full-devtools behaviour: the class refuses to dispose a non-existent Solid render tree and surfaces the asymmetry as a hard error rather than silently swallowing it.","triggerScenarios":"Calling `panel.unmount()` without a successful prior `mount()`, or calling it twice on the same instance. Common in asymmetric lifecycle code where cleanup is registered unconditionally but mount is conditional, or in StrictMode where the cleanup of a never-mounted dev effect runs.","commonSituations":"React StrictMode double-cleanups; conditionally rendered panels whose cleanup always fires; route transitions that call `unmount` on a panel that was already unmounted by the route guard; HMR running cleanups against stale instances.","solutions":["Track mount state locally and only unmount what you mounted.","Wrap in try/catch when the lifecycle is not fully under your control: `try { panel.unmount() } catch {}`.","Ensure symmetric mount/unmount — one `unmount()` per successful `mount()`.","Prefer the framework-specific `<ReactQueryDevtoolsPanel />` component to avoid manual lifecycle management."],"exampleFix":"// before - unmount may run on a never-mounted panel\nuseEffect(() => {\n  return () => panel.unmount()\n}, [])\n\n// after - guard the unmount\nlet mounted = false\npanel.mount(el); mounted = true\n// ...\nif (mounted) { panel.unmount(); mounted = false }","handlingStrategy":"validation","validationCode":"let panelMounted = false\nuseEffect(() => {\n  panel.mount(el); panelMounted = true\n  return () => { if (panelMounted) { panel.unmount(); panelMounted = false } }\n}, [])","typeGuard":"const isPanelMounted = (p: { unmount(): void }) =>\n  Boolean((p as unknown as { _isMounted?: boolean })._isMounted)","tryCatchPattern":"try {\n  panel.unmount()\n} catch (e) {\n  if (e.message === 'Devtools is not mounted') return\n  throw e\n}","preventionTips":["Keep mount/unmount symmetric for the panel: one unmount per successful mount.","Track your own mounted flag when lifecycles are not fully under your control.","Prefer <ReactQueryDevtoolsPanel /> over the vanilla panel class.","Avoid registering cleanups for mounts that may have been skipped."],"tags":["devtools","panel","unmount","lifecycle","vanilla"],"backgroundTag":null,"analyzedSha":"159982c80b844487054155c38ffc760fe4208daf","analyzedAt":"2026-08-12T16:21:52.822Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}