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

  1. Pair every `mount()` with an `unmount()` in cleanup: `useEffect(() => { panel.mount(el); return () => panel.unmount() }, [])`.
  2. Create a new `TanstackQueryDevtoolsPanel` instance per mount target.
  3. Guard with your own `isMounted` flag before calling `mount()`.
  4. 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

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


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