{"record":{"id":"f12920b88a410ee1","repo":"TanStack/query","slug":"devtools-is-already-mounted","errorCode":null,"errorMessage":"Devtools is already mounted","messagePattern":"Devtools is already mounted","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/query-devtools/src/TanstackQueryDevtools.tsx","lineNumber":95,"sourceCode":"  setInitialIsOpen(isOpen: boolean) {\n    this.#initialIsOpen[1](isOpen)\n  }\n\n  setErrorTypes(errorTypes: Array<DevtoolsErrorType>) {\n    this.#errorTypes[1](errorTypes)\n  }\n\n  setClient(client: QueryClient) {\n    this.#client[1](client)\n  }\n\n  setTheme(theme?: Theme) {\n    this.#theme[1](theme)\n  }\n\n  mount<T extends HTMLElement>(el: T) {\n    if (this.#isMounted) {\n      throw new Error('Devtools is already mounted')\n    }\n    const dispose = render(() => {\n      const [btnPosition] = this.#buttonPosition\n      const [pos] = this.#position\n      const [isOpen] = this.#initialIsOpen\n      const [errors] = this.#errorTypes\n      const [hideDisabledQueries] = this.#hideDisabledQueries\n      const [queryClient] = this.#client\n      const [theme] = this.#theme\n      let Devtools: DevtoolsComponentType\n\n      if (this.#Component) {\n        Devtools = this.#Component\n      } else {\n        Devtools = lazy(() => import('./DevtoolsComponent'))\n        this.#Component = Devtools\n      }\n","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/TanStack/query/blob/159982c80b844487054155c38ffc760fe4208daf/packages/query-devtools/src/TanstackQueryDevtools.tsx#L77-L113","documentation":"Thrown by `TanstackQueryDevtools.mount()` (the non-framework, framework-agnostic devtools class) when `mount` is called on an instance that is already mounted. The class tracks a single `#isMounted` flag and renders into exactly one DOM element via Solid's `render`; calling `mount` twice would create a leaked Solid disposal tree and a duplicate panel, so it throws to fail fast.","triggerScenarios":"Calling `devtools.mount(el)` twice on the same `TanstackQueryDevtools` instance without an intervening `unmount()`. Common in React StrictMode (which double-invokes effects in dev) when the devtools is instantiated outside the effect and `mount` is called in the effect body, or when a micro-frontend / widget host remounts the devtools on route change without disposing.","commonSituations":"React 18 StrictMode double-mounting effects; HMR reloading a module that holds a long-lived devtools instance; manually integrating the vanilla devtools class into a lifecycle that re-runs (e.g. inside `useEffect` without an unmount cleanup, or inside a `connectedCallback` that fires twice).","solutions":["Call `unmount()` in the same lifecycle's cleanup before `mount()` can re-run, e.g. `useEffect(() => { devtools.mount(el); return () => devtools.unmount() }, [])`.","Guard the call yourself: `if (!devtoolsMounted) { devtools.mount(el); devtoolsMounted = true }`.","Create a fresh `new TanstackQueryDevtools(config)` instance for each mount target instead of reusing one.","For React, prefer the `<ReactQueryDevtools />` component over the vanilla class — it manages mount/unmount for you."],"exampleFix":"// before - double mount in StrictMode\nuseEffect(() => {\n  devtools.mount(el)\n}, [])\n\n// after - pair mount with unmount\nuseEffect(() => {\n  devtools.mount(el)\n  return () => devtools.unmount()\n}, [])","handlingStrategy":"validation","validationCode":"// Track mount state alongside the devtools instance\nlet devtoolsMounted = false\nconst ensureMounted = (devtools, el) => {\n  if (devtoolsMounted) return\n  devtools.mount(el)\n  devtoolsMounted = true\n}","typeGuard":"// If you mirror the class, expose its flag instead of guessing\ntype MountedDevtools = { mount(el: HTMLElement): void; unmount(): void }\nconst isMountable = (d: MountedDevtools & { isMounted?: boolean }) =>\n  !(d as { __mounted?: boolean }).__mounted","tryCatchPattern":"try {\n  devtools.mount(el)\n} catch (e) {\n  if (e.message === 'Devtools is already mounted') {\n    // already mounted, nothing to do\n  } else throw e\n}","preventionTips":["Always pair mount() with unmount() in the same effect lifecycle.","Prefer the framework-specific <ReactQueryDevtools /> component which handles mount/unmount internally.","Create a fresh TanstackQueryDevtools instance per mount target rather than reusing one.","Guard against React StrictMode double-mount by tracking your own mounted flag."],"tags":["devtools","mount","lifecycle","solid","vanilla"],"backgroundTag":null,"analyzedSha":"159982c80b844487054155c38ffc760fe4208daf","analyzedAt":"2026-08-12T16:21:52.822Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}