solidjs/solid · warning
cleanups created outside a `createRoot` or `render` will nev
Error message
cleanups created outside a `createRoot` or `render` will never be run
What it means
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.
Source
Thrown at packages/solid/src/reactive/signal.ts:1018
* @param fn an effect that should run only once on mount
*
* @description https://docs.solidjs.com/reference/lifecycle/on-mount
*/
export function onMount(fn: () => void) {
createEffect(() => untrack(fn));
}
/**
* Runs an effect once before the reactive scope is disposed
* @param fn an effect that should run only once on cleanup
*
* @returns the same {@link fn} function that was passed in
*
* @description https://docs.solidjs.com/reference/lifecycle/on-cleanup
*/
export function onCleanup<T extends () => any>(fn: T): T {
if (Owner === null)
IS_DEV && console.warn("cleanups created outside a `createRoot` or `render` will never be run");
else if (Owner.cleanups === null) Owner.cleanups = [fn];
else Owner.cleanups.push(fn);
return fn;
}
/**
* Runs an effect whenever an error is thrown within the context of the child scopes
* @param fn boundary for the error
* @param handler an error handler that receives the error
*
* * If the error is thrown again inside the error handler, it will trigger the next available parent handler
*
* @description https://docs.solidjs.com/reference/reactive-utilities/catch-error
*/
export function catchError<T>(fn: () => T, handler: (err: Error) => void) {
ERROR || (ERROR = Symbol("error"));
Owner = createComputation(undefined!, undefined, true);
Owner.context = { ...Owner.context, [ERROR]: [handler] };View on GitHub (pinned to f47845f9cc)
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
Example fix
// before
const t = setInterval(tick, 1000);
onCleanup(() => clearInterval(t)); // module scope: never runs
// after
const App = () => {
const t = setInterval(tick, 1000);
onCleanup(() => clearInterval(t));
return <Foo />;
}; Defensive patterns
Strategy: validation
Validate before calling
import { getOwner } from 'solid-js';
if (getOwner()) onCleanup(fn); else registerManualCleanup(fn); Type guard
import { getOwner } from 'solid-js';
const insideOwner = (): boolean => getOwner() !== null; Prevention
- 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
When it happens
Trigger: Calling onCleanup(fn) at module scope, inside top-level awaits outside the tree, or in code executed before render()/createRoot establishes an owner.
Common situations: Timer/interval setup in module-level code; web worker scripts sharing utilities with components; refactoring component logic into standalone functions losing the owner context.
Related errors
- computations created outside a `createRoot` or `render` will
- error handlers created outside a `createRoot` or `render` wi
- Dispose method must be an explicit argument to createRoot fu
- You appear to have multiple instances of Solid. This can lea
- Cannot mutate a Store directly
AI-assisted analysis of solidjs/solid@f47845f9cc (2026-08-27).
Data as JSON: /api/errors/f3dd5e4db7841b88.
Report an issue: GitHub.