{"record":{"id":"f3dd5e4db7841b88","repo":"solidjs/solid","slug":"cleanups-created-outside-a-createroot-or-render","errorCode":null,"errorMessage":"cleanups created outside a `createRoot` or `render` will never be run","messagePattern":"cleanups created outside a `createRoot` or `render` will never be run","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/solid/src/reactive/signal.ts","lineNumber":1018,"sourceCode":" * @param fn an effect that should run only once on mount\n *\n * @description https://docs.solidjs.com/reference/lifecycle/on-mount\n */\nexport function onMount(fn: () => void) {\n  createEffect(() => untrack(fn));\n}\n\n/**\n * Runs an effect once before the reactive scope is disposed\n * @param fn an effect that should run only once on cleanup\n *\n * @returns the same {@link fn} function that was passed in\n *\n * @description https://docs.solidjs.com/reference/lifecycle/on-cleanup\n */\nexport function onCleanup<T extends () => any>(fn: T): T {\n  if (Owner === null)\n    IS_DEV && console.warn(\"cleanups created outside a `createRoot` or `render` will never be run\");\n  else if (Owner.cleanups === null) Owner.cleanups = [fn];\n  else Owner.cleanups.push(fn);\n  return fn;\n}\n\n/**\n * Runs an effect whenever an error is thrown within the context of the child scopes\n * @param fn boundary for the error\n * @param handler an error handler that receives the error\n *\n * * If the error is thrown again inside the error handler, it will trigger the next available parent handler\n *\n * @description https://docs.solidjs.com/reference/reactive-utilities/catch-error\n */\nexport function catchError<T>(fn: () => T, handler: (err: Error) => void) {\n  ERROR || (ERROR = Symbol(\"error\"));\n  Owner = createComputation(undefined!, undefined, true);\n  Owner.context = { ...Owner.context, [ERROR]: [handler] };","sourceCodeStart":1000,"sourceCodeEnd":1036,"githubUrl":"https://github.com/solidjs/solid/blob/f47845f9cc16ecbb316aa6560c7161f45af9a3d8/packages/solid/src/reactive/signal.ts#L1000-L1036","documentation":"onCleanup registers cleanup on the current Owner. When Owner is null there is no disposal lifecycle, so the cleanup would never run; Solid emits this dev-only console warning rather than dropping the callback silently. The function is still returned, but its cleanup intent is void.","triggerScenarios":"Calling onCleanup(fn) at module scope, inside top-level awaits outside the tree, or in code executed before render()/createRoot establishes an owner.","commonSituations":"Timer/interval setup in module-level code; web worker scripts sharing utilities with components; refactoring component logic into standalone functions losing the owner context.","solutions":["Move the onCleanup call inside a component body or a createRoot","Manage cleanup manually with try/finally or explicit dispose functions in non-reactive code","Use createRoot(dispose => ...) and call dispose yourself for standalone reactive islands"],"exampleFix":"// before\nconst t = setInterval(tick, 1000);\nonCleanup(() => clearInterval(t)); // module scope: never runs\n\n// after\nconst App = () => {\n  const t = setInterval(tick, 1000);\n  onCleanup(() => clearInterval(t));\n  return <Foo />;\n};","handlingStrategy":"validation","validationCode":"import { getOwner } from 'solid-js';\nif (getOwner()) onCleanup(fn); else registerManualCleanup(fn);","typeGuard":"import { getOwner } from 'solid-js';\nconst insideOwner = (): boolean => getOwner() !== null;","tryCatchPattern":null,"preventionTips":["Register cleanup inside components or createRoot","For non-reactive code, manage cleanup manually (dispose lists, finally)","Never set timers/listeners at module scope expecting onCleanup to fire"],"tags":["solid","oncleanup","owner","memory-leak","dev-warning"],"backgroundTag":"lifecycle-outside-reactive-root","analyzedSha":"f47845f9cc16ecbb316aa6560c7161f45af9a3d8","analyzedAt":"2026-08-27T05:39:24.283Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}