TanStack/query · error
Devtools is not mounted
Error message
Devtools is not mounted
What it means
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.
Source
Thrown at packages/query-devtools/src/TanstackQueryDevtoolsPanel.tsx:165
return hideDisabledQueries()
},
get onClose() {
return onClose()
},
get theme() {
return theme()
},
}}
/>
)
}, el)
this.#isMounted = true
this.#dispose = dispose
}
unmount() {
if (!this.#isMounted) {
throw new Error('Devtools is not mounted')
}
this.#dispose?.()
this.#isMounted = false
}
}
export { TanstackQueryDevtoolsPanel }
View on GitHub (pinned to 159982c80b)
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.
Example fix
// before - unmount may run on a never-mounted panel
useEffect(() => {
return () => panel.unmount()
}, [])
// after - guard the unmount
let mounted = false
panel.mount(el); mounted = true
// ...
if (mounted) { panel.unmount(); mounted = false } Defensive patterns
Strategy: validation
Validate before calling
let panelMounted = false
useEffect(() => {
panel.mount(el); panelMounted = true
return () => { if (panelMounted) { panel.unmount(); panelMounted = false } }
}, []) Type guard
const isPanelMounted = (p: { unmount(): void }) =>
Boolean((p as unknown as { _isMounted?: boolean })._isMounted) Try / catch
try {
panel.unmount()
} catch (e) {
if (e.message === 'Devtools is not mounted') return
throw e
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Devtools is not mounted
- Devtools is already mounted
- Devtools is already mounted
- No QueryClient found
- No QueryClient found
AI-assisted analysis of TanStack/query@159982c80b (2026-08-12).
Data as JSON: /api/errors/d484ffa9e3bf5b82.
Report an issue: GitHub.