TanStack/query · error
Devtools is already mounted
Error message
Devtools is already mounted
What it means
Thrown by `TanstackQueryDevtoolsPanel.mount()` (the standalone panel variant of the vanilla devtools class, used for embedding the devtools panel without the floating button) when `mount` is called on an already-mounted instance. Same single-mount constraint as the full devtools: the class renders Solid into one container and tracks a single `#isMounted` flag.
Source
Thrown at packages/query-devtools/src/TanstackQueryDevtoolsPanel.tsx:103
setErrorTypes(errorTypes: Array<DevtoolsErrorType>) {
this.#errorTypes[1](errorTypes)
}
setClient(client: QueryClient) {
this.#client[1](client)
}
setOnClose(onClose: () => void) {
this.#onClose[1](() => onClose)
}
setTheme(theme?: Theme) {
this.#theme[1](theme)
}
mount<T extends HTMLElement>(el: T) {
if (this.#isMounted) {
throw new Error('Devtools is already mounted')
}
const dispose = render(() => {
const [btnPosition] = this.#buttonPosition
const [pos] = this.#position
const [isOpen] = this.#initialIsOpen
const [errors] = this.#errorTypes
const [hideDisabledQueries] = this.#hideDisabledQueries
const [queryClient] = this.#client
const [onClose] = this.#onClose
const [theme] = this.#theme
let Devtools: DevtoolsComponentType
if (this.#Component) {
Devtools = this.#Component
} else {
Devtools = lazy(() => import('./DevtoolsPanelComponent'))
this.#Component = Devtools
}View on GitHub (pinned to 159982c80b)
Solutions
- Pair every `mount()` with an `unmount()` in cleanup: `useEffect(() => { panel.mount(el); return () => panel.unmount() }, [])`.
- Create a new `TanstackQueryDevtoolsPanel` instance per mount target.
- Guard with your own `isMounted` flag before calling `mount()`.
- If using the panel inside React, prefer `<ReactQueryDevtoolsPanel />` which manages mount/unmount internally.
Example fix
// before - panel mounted twice on route change panel.mount(hostEl) // after - unmount before remounting panel.unmount() panel.mount(hostEl)
Defensive patterns
Strategy: validation
Validate before calling
let panelMounted = false
const mountPanel = (panel, el) => {
if (panelMounted) return
panel.mount(el)
panelMounted = true
} Type guard
const isPanelMountable = (p: { mount(el: HTMLElement): unknown }) =>
!(p as unknown as { _isMounted?: boolean })._isMounted Try / catch
try {
panel.mount(el)
} catch (e) {
if (e.message === 'Devtools is already mounted') return
throw e
} Prevention
- Pair panel.mount() with panel.unmount() in the same effect.
- Prefer the framework-specific <ReactQueryDevtoolsPanel /> component.
- Create a new TanstackQueryDevtoolsPanel instance per mount target.
- Guard mount() with your own mounted flag in StrictMode or remount-heavy lifecycles.
When it happens
Trigger: Calling `panel.mount(el)` twice on the same `TanstackQueryDevtoolsPanel` instance without an intervening `unmount()`. Typical when embedding the panel in a custom container whose mount effect re-runs (StrictMode, route change, HMR) or when reusing a panel instance across views.
Common situations: React 18 StrictMode double-mounting; route-based remounts in SPAs that reuse one panel instance; embedding the panel inside a web component whose `connectedCallback` fires more than once across reconnections.
Related errors
- Devtools is already mounted
- Devtools is not mounted
- Devtools is not mounted
- No QueryClient found
- No QueryClient found
AI-assisted analysis of TanStack/query@159982c80b (2026-08-12).
Data as JSON: /api/errors/4e9da4c5c0457244.
Report an issue: GitHub.