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,
    time

View on GitHub (pinned to f47845f9cc)

Solutions

  1. Find the effect that writes a signal it also reads and break the cycle: move the write to an event handler or async boundary
  2. Use untrack(() => setter(...)) when the write is intentionally not a dependency
  3. 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

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


AI-assisted analysis of solidjs/solid@f47845f9cc (2026-08-27). Data as JSON: /api/errors/38c61f103af35d64. Report an issue: GitHub.