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

  1. Track mount state locally and only unmount what you mounted.
  2. Wrap in try/catch when the lifecycle is not fully under your control: `try { panel.unmount() } catch {}`.
  3. Ensure symmetric mount/unmount — one `unmount()` per successful `mount()`.
  4. 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

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


AI-assisted analysis of TanStack/query@159982c80b (2026-08-12). Data as JSON: /api/errors/d484ffa9e3bf5b82. Report an issue: GitHub.