solidjs/solid · critical · Error
Potential Infinite Loop Detected.
Error message
Potential Infinite Loop Detected.
What it means
Solid detects that its internal update queue has exceeded one million entries, which is practically only reachable via an infinite reactive loop: a computation synchronously writing a signal it also reads. It clears the queue and (in dev) throws with a descriptive message so the loop is attributable rather than freezing the tab.
Source
Thrown at packages/solid/src/reactive/signal.ts:1369
if (!TransitionRunning) node.value = value;
} else node.value = value;
if (node.observers && node.observers.length) {
runUpdates(() => {
for (let i = 0; i < node.observers!.length; i += 1) {
const o = node.observers![i];
const TransitionRunning = Transition && Transition.running;
if (TransitionRunning && Transition!.disposed.has(o)) continue;
if (TransitionRunning ? !o.tState : !o.state) {
if (o.pure) Updates!.push(o);
else Effects!.push(o);
if ((o as Memo<any>).observers) markDownstream(o as Memo<any>);
}
if (!TransitionRunning) o.state = STALE;
else o.tState = STALE;
}
if (Updates!.length > 10e5) {
Updates = [];
if (IS_DEV) throw new Error("Potential Infinite Loop Detected.");
throw new Error();
}
}, false);
}
}
return value;
}
function updateComputation(node: Computation<any>) {
if (!node.fn) return;
cleanNode(node);
const time = ExecCount;
runComputation(
node,
Transition && Transition.running && Transition.sources.has(node as Memo<any>)
? (node as Memo<any>).tValue
: node.value,
timeView on GitHub (pinned to f47845f9cc)
Solutions
- Find the effect that writes a signal it also reads and break the cycle: move the write to an event handler or async boundary
- Use untrack(() => setter(...)) when the write is intentionally not a dependency
- Restructure derived values as createMemo instead of effect+signal pairs
Example fix
// before const [count, setCount] = createSignal(0); createEffect(() => setCount(count() + 1)); // infinite loop // after const [count, setCount] = createSignal(0); const doubled = createMemo(() => count() * 2); // derive, don't write
Defensive patterns
Strategy: validation
Validate before calling
// Pattern-check effects before shipping: an effect must not synchronously write a signal it reads
const writes = collectSetters(effectBody); // lint/AST step in CI
if (writes.intersects(collectReads(effectBody))) fail('effect writes its own dependency'); Try / catch
try { runApp(); } catch (e) { if (/Infinite Loop/i.test(String(e))) locateSelfWritingEffect(); else throw e; } Prevention
- Never call a setter synchronously inside an effect that reads the same signal
- Derive values with createMemo instead of effect + signal
- Use untrack around intentional writes inside computations
When it happens
Trigger: createEffect(() => setCount(count() + 1)); or any effect/memo that synchronously sets a signal in its own dependency chain; mutual effects that ping-pong writes; writing to a store inside a memo that reads the same store path.
Common situations: Porting React setState-in-render patterns; accidentally calling a setter inside a createMemo; forgetting untrack/batch around a write triggered from a computation; infinite loop between two effects each updating the other's dependency.
Related errors
- Expected the observer to be an object.
- Dispose method must be an explicit argument to createRoot fu
- Expected the observer to be an object.
- getContextId cannot be used under non-hydrating context
- getNextContextId cannot be used under non-hydrating context
AI-assisted analysis of solidjs/solid@f47845f9cc (2026-08-27).
Data as JSON: /api/errors/38c61f103af35d64.
Report an issue: GitHub.