Skip to content

Hydration re-asks an async memo the server answered with an error, when only isPending of a dependent is read #4375

Closed

Description

@ryansolid

Found by the async app generator's hydration phase (#4371). Reproduces on next 5f60713. This breaks the no-refetch-on-hydration rule: a read the server answered, with a value or an error, is never asked again by a hydrating client.

const [s] = createSignal(1);
function App() {
  // rejects on the server: 1 + latest(s) + s() = 3
  const m0 = createMemo(() => fetchM0(1 + latest(s) + s()));
  const m1 = createMemo(() => fetchM1(1 + m0()));
  return (
    <main>
      <Errored fallback={e => "[ROOT-E]"}>
        <Loading fallback="[ROOT-L]">
          <span>{String(isPending(m1))}</span>
        </Loading>
      </Errored>
    </main>
  );
}

The server streams m0's rejection (_$HY.r["0"] settles with Error("m0:3")) together with the Loading fragment <span>false</span>. When the client hydrates the shell and that chunk then arrives, it calls fetchM0(3) again for real, outside hydration's dry run. No client write happened before that call.

The generator's minimizer kept the latest(s) dependency: with m0 over s() alone, the case doesn't fail. The page only reads isPending(m1), so no Errored ever sees the error.

Activity

  1. ryansolid commented on Oct 28, 2026

    @ryansolid
    MemberAuthor

    Fixed on next by #4385 (bd95616); it ships in the next RC.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions