TanStack/query · error

Devtools is not mounted

Error message

Devtools is not mounted

What it means

Thrown by `TanstackQueryDevtools.unmount()` when `unmount` is called on an instance whose `#isMounted` flag is `false`. The class refuses to invoke the internal Solid `#dispose` handle when nothing is mounted because that handle would be `undefined`, and calling it would either no-op misleadingly or, after a future change, throw an unrelated TypeError. It fails fast to surface the lifecycle bug.

Source

Thrown at packages/query-devtools/src/TanstackQueryDevtools.tsx:153

              return errors()
            },
            get hideDisabledQueries() {
              return hideDisabledQueries()
            },
            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 { TanstackQueryDevtools }

View on GitHub (pinned to 159982c80b)

Solutions

  1. Track mount state yourself and only unmount what you mounted: `useEffect(() => { devtools.mount(el); return () => { if (mounted) devtools.unmount() } })`.
  2. Wrap the call: `try { devtools.unmount() } catch { /* not mounted, ignore */ }` when the lifecycle is outside your control.
  3. Reproduce the lifecycle symmetrically — ensure every `mount()` path has exactly one matching `unmount()`.
  4. Switch to the framework-specific devtools component (`<ReactQueryDevtools />`) which handles this internally.

Example fix

// before - cleanup may run without a matching mount
useEffect(() => {
  return () => devtools.unmount()
}, [])

// after - only unmount what we mounted
useEffect(() => {
  let mounted = false
  devtools.mount(el)
  mounted = true
  return () => { if (mounted) devtools.unmount() }
}, [])
Defensive patterns

Strategy: validation

Validate before calling

// Track mount state so unmount only runs when mounted
let mounted = false
useEffect(() => {
  devtools.mount(el); mounted = true
  return () => { if (mounted) { devtools.unmount(); mounted = false } }
}, [])

Type guard

const isMounted = (d: { mount(el: HTMLElement): unknown; unmount(): void }) =>
  Boolean((d as unknown as { _isMounted?: boolean })._isMounted)

Try / catch

try {
  devtools.unmount()
} catch (e) {
  if (e.message === 'Devtools is not mounted') {
    // no-op: nothing to clean up
  } else throw e
}

Prevention

When it happens

Trigger: Calling `devtools.unmount()` without a prior successful `mount()` on the same instance, or calling `unmount()` twice (the flag is reset to `false` after the first call). Frequent in effect cleanups that run on unmount of a component that never mounted the devtools, or in StrictMode where the dev cleanup fires for an effect whose dev mount was skipped/failed.

Common situations: React StrictMode double-invoking cleanups; conditionally-rendered devtools where the cleanup runs but the mount branch was not taken; calling `unmount` in a `disconnectCallback` / `willDestroy` hook of an element that was never connected; HMR re-running cleanup on a stale instance.

Related errors


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