SIGN IN SIGN UP

Fix: Treat incomplete tree as an error during recovery (#36911)

In `recoverFromConcurrentError`, there's logic to determine if the
latest render attempt resulted in another error or if it completed
successfully, by checking whether the exit status matches RootErrored.

The logic was incomplete because RootSuspendedAtTheShell is also
considered an errored state in this context. This can cause an
incomplete tree (i.e. one that unwinds without entering the complete
phase) to be mistaken for a complete one, leading to confusing errors.
An incomplete tree can never be committed because it's not guaranteed to
represent a coherent state.

An example of how this can manifest as a bug: in #33580, an error caught
by an error boundary triggers a synchronous recovery render. During that
render a parent component suspends on `use(thenable)` with no Suspense
boundary above it, so the tree unwinds to the shell instead of
completing. Because the incomplete tree is mistaken for a recovered one
and committed, the parent fiber becomes current with a truncated hook
list — only the hooks it rendered before suspending. On the next render
it calls the rest of its hooks and throws "Rendered more hooks than
during the previous render."

Fixes #33580.

Co-authored-by: Arunanshu Biswas
<48434243+arunanshub@users.noreply.github.com>

Co-authored-by: Arunanshu Biswas <48434243+arunanshub@users.noreply.github.com>
A
Andrew Clark committed
c3555f0ca2648380ccd3d6af23479610e72f6bf1
Parent: 2c8b735
Committed by GitHub <noreply@github.com> on 6/30/2026, 7:38:59 PM