fix(signals): don't route an already-caught error past its Errored through a Loading - #2856
Merged
ryansolid merged 1 commit intoJul 9, 2026
Conversation
…rough a Loading In Errored > Loading > Errored > content, a sync error from the content was routed past the inner Errored to the boundary above the Loading — both when the content threw during the same flush the boundaries mounted and when it threw reactively after a healthy commit. With no outer boundary, the error escaped entirely and halted reactivity. This broke the React-style Suspense > ErrorBoundary > content nesting that TanStack Router mirrors. The inner Errored consumed the ERROR dimension from the notification mask when it caught and forwarded only the PENDING remainder up the queue chain, but the Loading queue's notify-through remap keyed off the node's raw status flags and resurrected the already-caught error past its handler. The remap now fires only while the ERROR dimension is still live in the mask. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: b3c1c16 The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Merging this PR will degrade performance by 55.9%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | merge |
73.6 µs | 166.8 µs | -55.9% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing brenelz:fix/loading-errored-composition-escape (b3c1c16) with next (ef4d53e)
ryansolid
added a commit
that referenced
this pull request
Jul 9, 2026
After #2856 narrowed the remap to fire only while the ERROR dimension was still live in the notification mask, the rule collapsed into the generic consume-and-forward path: each boundary consumes only its own status dimension from the mask and forwards the remainder, so an error inside a Loading reaches its Errored natively. The only residual behavior — suppressing pending collection on a node that is simultaneously pending and errored — is unreachable: status propagation (notifyStatus) assigns a single dimension per node, and the effect-phase error path notifies with literal ERROR-only flags. Pins the two paths the remap used to intercept (sync error in the mounting flush and reactive error after a healthy commit, both Errored > Loading > content). Full signals, solid, and solid-web (client/SSR/hydration) suites pass. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
In the
Errored > Loading > Errored > contentcomposition, a sync error thrown by the content was routed past the innerErroredto the boundary above theLoading— the outer fallback replaced the whole subtree, and with no outer boundary the error escaped entirely and (new in beta.16) permanently halted reactivity. This broke the React-styleSuspense > ErrorBoundary > contentnesting that TanStack Router mirrors; it worked on 2.0.0-beta.15 and regressed in beta.16 with the #2809 boundary notify-through work.It reproduced both when the content threw during the same flush the boundaries mounted (e.g. navigating to a route that renders already-errored) and when it threw reactively after a healthy commit.
Root cause
When the inner
Erroredcatches,CollectionQueue#notifyconsumes the ERROR dimension from the notification mask (type) and forwards only the PENDING remainder up the queue chain. But theLoadingqueue's notify-through remap keyed off the node's raw status flags instead of the mask — it saw "this node has an error", remapped the PENDING-only remainder back into an ERROR notification, and forwarded the already-caught error past its handler.Fix
The remap now additionally requires the ERROR dimension to still be live in the mask (
type & STATUS_ERROR), i.e. it only fires while the error is still unhandled — which is exactly theErrored > Loading > erroring contentcase the rule was written for (#2821).Testing
createErrorBoundary.test.tspinning the composition: throw in the mounting flush, reactive throw after a healthy commit, and an async rejection — all must catch at the inner boundary.errored-async-nestingpins that motivated the notify-through rule.@solidjs/signals2.0.0-beta.15 catches at the inner boundary and 2.0.0-beta.16 routes to the outer boundary in the same scenarios.🤖 Generated with Claude Code