solidjs/solid · error

Attempting to access a stale value from <${name}> that could

Error message

Attempting to access a stale value from <${name}> that could possibly be undefined. This may occur because you are reading the accessor returned from the component at a time where it has already been unmounted. We recommend cleaning up any stale timers or async, or reading from the initial condition.

What it means

In non-keyed <Show>, the child receives an accessor rather than a value. If Show's condition flips or the component unmounts while that accessor is read (e.g. inside an async callback or stray timer), Solid throws the narrowedError('Show') to reveal the read-after-unmount. The message recommends cleaning up stale async work or reading only while mounted.

Source

Thrown at packages/solid/src/render/flow.ts:137

          ? {
              equals: (a, b) => !a === !b,
              name: "condition"
            }
          : { equals: (a, b) => !a === !b }
      );
  return createMemo(
    () => {
      const c = condition();
      if (c) {
        const child = props.children;
        const fn = typeof child === "function" && child.length > 0;
        return fn
          ? untrack(() =>
              (child as any)(
                keyed
                  ? (c as T)
                  : () => {
                      if (!untrack(condition)) throw narrowedError("Show");
                      return conditionValue();
                    }
              )
            )
          : child;
      }
      return props.fallback;
    },
    undefined,
    IS_DEV ? { name: "value" } : undefined
  ) as unknown as JSX.Element;
}

type EvalConditions = readonly [number, Accessor<unknown>, MatchProps<unknown>];

/**
 * Switches between content based on mutually exclusive conditions
 * ```typescript

View on GitHub (pinned to f47845f9cc)

Solutions

  1. Capture the value, not the accessor: use {(v) => <Child value={v()}>} rendered immediately
  2. Clean up async work with onCleanup so no reads happen after unmount
  3. Switch to keyed mode <Show when={x} keyed> if the child genuinely needs to hold the value across time

Example fix

// before
<Show when={user()}>
  {(user) => {
    setTimeout(() => api.track(user()), 5000); // accessor read after flip
    return <Profile user={user()} />;
  }}
</Show>

// after
<Show when={user()} keyed>
  {(u) => {
    setTimeout(() => api.track(u), 5000); // value captured
    return <Profile user={u} />;
  }}
</Show>
Defensive patterns

Strategy: validation

Validate before calling

// capture the value immediately; never retain the accessor
<Show when={user()} keyed>
  {(u) => {
    onCleanup(() => cancelPending());
    return <Profile user={u} />;
  }}
</Show>

Type guard

// TS-only habit: type the child as taking the value, forcing keyed
function child(u: User) { ... }
<Show when={user()} keyed>{(u) => child(u)}</Show>

Try / catch

try { const v = accessor(); } catch (e) { if (/stale value/.test(String(e))) return null; /* branch no longer active */ throw e; }

Prevention

When it happens

Trigger: Non-keyed <Show when={x}>{(v) => ...}</Show> where the v accessor is captured and called later in a setTimeout, event handler, or promise callback after the condition changed; reading v outside untrack during re-evaluation.

Common situations: Passing the accessor into long-lived callbacks instead of the current value; forgetting onCleanup for async tasks; migration from keyed to non-keyed Show changing the child arg from value to accessor.

Related errors


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