Skip to content

fix: call onerror and provide a working reset when hydrating a failed boundary - #18556

Merged
dummdidumm merged 2 commits into
sveltejs:mainfrom
Nic-Polumeyv:fix-hydrated-boundary-reset
Jul 23, 2026
Merged

dummdidumm merged 2 commits into
sveltejs:mainfrom
Nic-Polumeyv:fix-hydrated-boundary-reset

Conversation

@Nic-Polumeyv

Copy link
Copy Markdown
Member

Fixes #18555

A boundary that failed during SSR hydrates via #hydrate_failed_content, which never calls onerror and passes the failed snippet a no-op reset. Both came in with #17672, whose docs say onerror "will be called upon hydration with the deserialized error object". Once hydrated as failed, the boundary can never leave that state. Downstream this is what keeps SvelteKit's +error.svelte mounted after navigating away from a server-rendered error page (sveltejs/kit#16345).

This extracts the reset/onerror machinery from #handle_error into #create_reset and uses it in the hydration path too. onerror is invoked in a microtask because it may mutate state, which is disallowed while hydrating. #handle_error already invokes it asynchronously, so the timing matches the error path.

The tests flip a recovered flag before calling reset, since the child would otherwise throw again. That is the intended retry pattern, and the same one kit uses when it resets route boundaries on navigation. Verified against kit end to end, hydrating a server-rendered error page and navigating away now tears it down with no kit changes needed.

Before submitting the PR, please make sure you do the following

  • It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
  • Prefix your PR title with feat:, fix:, chore:, or docs:.
  • This message body should clearly illustrate what problems it solves.
  • Ideally, include a test that fails without this PR but passes with it.
  • If this PR changes code within packages/svelte/src, add a changeset (npx changeset).

Tests and linting

  • Run the tests with pnpm test and lint the project with pnpm lint

@pkg-svelte-dev

pkg-svelte-dev Bot commented Jul 17, 2026 •

Copy link
Copy Markdown

Install the latest version of svelte from 906d82f:

pnpm add https://pkg.svelte.dev/svelte/c/906d82fc4eeb7234d929c274ee3ed7ba0eca7cd5

Open in pkg.svelte.dev: https://pkg.svelte.dev/repos/svelte/pr/18556

Note

This PR is from a fork. A maintainer must approve approve each commit before it can be built and installed.

@changeset-bot

changeset-bot Bot commented Jul 17, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 906d82f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
svelte Patch

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

@Nic-Polumeyv

Copy link
Copy Markdown
Member Author

One asymmetry I noticed while writing this but left alone. In #handle_error the failed snippet is rendered with effect.b = this and BOUNDARY_EFFECT, and errors thrown inside it are routed through invoke_error_boundary. The hydration path renders it in a bare branch(), so an error thrown while hydrating a failed snippet propagates differently. Unifying them would have changed behavior beyond what this PR fixes, so I kept it out. Happy to follow up here or separately if you want them aligned.

@dummdidumm dummdidumm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you!

@dummdidumm
dummdidumm merged commit 3dde011 into sveltejs:main Jul 23, 2026
15 of 16 checks passed
@github-actions github-actions Bot mentioned this pull request Jul 23, 2026
dummdidumm pushed a commit that referenced this pull request Jul 24, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## svelte@5.56.8

### Patch Changes

- fix: call `onerror` and provide a working `reset` when hydrating a
failed boundary
([#18556](#18556))

- fix: preserve select selection when spread attributes omit value
([#18561](#18561))

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
khromov added a commit to khromov/mochi that referenced this pull request Jul 29, 2026
Bumps Svelte from **5.56.7 → 5.56.8** across every workspace.

## Upstream changes

Patch release, two fixes — the first lands directly on a Mochi code
path, since the preprocessor auto-wraps every `mochi:hydrate*` island in
a `<svelte:boundary>`:

- fix: call `onerror` and provide a working `reset` when hydrating a
failed boundary
([sveltejs/svelte#18556](sveltejs/svelte#18556))
- fix: preserve select selection when spread attributes omit value
([sveltejs/svelte#18561](sveltejs/svelte#18561))

## Bump table

| Workspace | Field | From | To |
| --- | --- | --- | --- |
| `packages/mochi` | devDependencies | `^5.56.7` | `^5.56.8` |
| `packages/mochi-rsvelte` | devDependencies | `^5.56.7` | `^5.56.8` |
| `packages/site` | dependencies | `^5.56.7` | `^5.56.8` |
| `packages/demos` | dependencies | `^5.56.7` | `^5.56.8` |
| `packages/docs` | dependencies | `^5.56.7` | `^5.56.8` |
| `packages/minimal` | dependencies | `^5.56.7` | `^5.56.8` |
| `packages/minimal-rsvelte` | dependencies | `^5.56.7` | `^5.56.8` |
| `packages/support` | dependencies | `^5.56.7` | `^5.56.8` |

## Deliberately not changed

- **`packages/mochi` `peerDependencies.svelte` stays at `^5.56.6`.**
It's a floor for published consumers and already admits 5.56.8; raising
it would force an upgrade for no API reason. Since published
`mochi-framework` carries Svelte only as a devDependency plus this
unchanged peer range, nothing consumer-visible moved — hence
`chore(deps):` rather than `fix(deps):`, and no release is warranted.
- Scope is Svelte only, not a full dependency sweep. Also currently
outdated and left alone: `@lucide/svelte` (1.25.0 → 1.27.0),
`@rsvelte/vite-plugin-svelte-native` (0.2.8 → 0.3.1), `svelte-check`
(4.7.3 → 4.7.4, exact-pinned because it carries a patch keyed on the
version), `typescript` (6.0.3 → 7.0.2).

The `bun.lock` diff is Svelte-only; the one removed `svelte/devalue`
entry is a dedupe artifact (devalue is now satisfied from the root).

## Verification

- **`bun run syncpack`** — ✅ no issues; cross-workspace agreement holds,
including the unchanged peer range.
- **`bun run checks`** (lint:fix + format + typecheck + test) — ✅ all
four stages green. Typecheck 0 errors across all workspaces; tests
142/142 mochi-framework files, 9/9 site, 2/2 support,
demos/minimal/minimal-rsvelte all green, 0 failures.
- **Clean reinstall + dedupe** — `rm -rf node_modules
packages/*/node_modules && bun install`.
`packages/*/node_modules/svelte` is empty and `node_modules/.bun` is
absent, so there is exactly one hoisted `svelte@5.56.8`. (Duplicate
copies mean two context maps and break `getContext` across the island
boundary — invisible to CI.)
- **`bun run build`** — ✅ output byte-identical to the pre-bump baseline
apart from timings. No optimization skipped, no shake fallback:

  | | before | after |
  | --- | --- | --- |
  | `svelte-shaker` (site) | slimmed 33 of 163 | slimmed 33 of 163 |
| site | 65 pages · 245 client files · 12 public · 7 image assets · 2
email templates | identical |
  | demos | 10 pages · 32 client files | identical |
  | support | 2 pages · 19 client files · 1 email template | identical |
| minimal / minimal-rsvelte | 1 page · 10 client files each | identical
|

  Per-file bundle sizes in the build listing are unchanged too.

- **Browser smoke test** (`PORT=4444 bun run dev:site`, driven via
chrome-devtools):
  - `/` — no console errors/warnings.
  - `/docs/queues/` — no console errors/warnings.
- `/demos/error-boundaries/` — the demo most relevant to #18556. All
five scenarios render their expected fallbacks (user-written boundary,
`mochi:hydrate` SSR throw, `mochi:hydrate` client throw, `mochi:defer`
server-island throw, healthy server island with an inner throwing
`mochi:hydrate` child). All 22 requests 200, including both
`/_mochi/island/*` fetches. The only console output is the demo's
intentional `ThrowOnClient` failure. Debug bar reports **Svelte
5.56.8**.
- `/demos/nested-islands/` — clean console, all four `/_mochi/island/*`
fetches 200.
- `/demos/is-hydratable/` — clean console, both island fetches 200. This
is the canary for the duplicate-copy failure mode (`isHydratable()` is
its first casualty), so it independently confirms the dedupe.
- **`bun run cli-test`** — ✅ pass (scaffold, install, test, typecheck).
`minimal` and `demos` both moved and double as `create-mochi` template
sources.
renovate Bot added a commit to cigaleapp/cigale that referenced this pull request Aug 2, 2026
##### [v5.56.8](https://github.com/sveltejs/svelte/blob/HEAD/packages/svelte/CHANGELOG.md#5568)

##### Patch Changes

- fix: call `onerror` and provide a working `reset` when hydrating a failed boundary ([#18556](sveltejs/svelte#18556))

- fix: preserve select selection when spread attributes omit value ([#18561](sveltejs/svelte#18561))
##### [v5.56.7](https://github.com/sveltejs/svelte/blob/HEAD/packages/svelte/CHANGELOG.md#5567)

##### Patch Changes

- chore: provide `indent` option for `print` ([#18474](sveltejs/svelte#18474))
renovate Bot added a commit to cigaleapp/cigale that referenced this pull request Aug 2, 2026
##### [v5.56.8](https://github.com/sveltejs/svelte/blob/HEAD/packages/svelte/CHANGELOG.md#5568)

##### Patch Changes

- fix: call `onerror` and provide a working `reset` when hydrating a failed boundary ([#18556](sveltejs/svelte#18556))

- fix: preserve select selection when spread attributes omit value ([#18561](sveltejs/svelte#18561))
##### [v5.56.7](https://github.com/sveltejs/svelte/blob/HEAD/packages/svelte/CHANGELOG.md#5567)

##### Patch Changes

- chore: provide `indent` option for `print` ([#18474](sveltejs/svelte#18474))
renovate Bot added a commit to cigaleapp/cigale that referenced this pull request Aug 2, 2026
##### [v5.56.8](https://github.com/sveltejs/svelte/blob/HEAD/packages/svelte/CHANGELOG.md#5568)

##### Patch Changes

- fix: call `onerror` and provide a working `reset` when hydrating a failed boundary ([#18556](sveltejs/svelte#18556))

- fix: preserve select selection when spread attributes omit value ([#18561](sveltejs/svelte#18561))
##### [v5.56.7](https://github.com/sveltejs/svelte/blob/HEAD/packages/svelte/CHANGELOG.md#5567)

##### Patch Changes

- chore: provide `indent` option for `print` ([#18474](sveltejs/svelte#18474))
renovate Bot added a commit to cigaleapp/cigale that referenced this pull request Aug 2, 2026
##### [v5.56.8](https://github.com/sveltejs/svelte/blob/HEAD/packages/svelte/CHANGELOG.md#5568)

##### Patch Changes

- fix: call `onerror` and provide a working `reset` when hydrating a failed boundary ([#18556](sveltejs/svelte#18556))

- fix: preserve select selection when spread attributes omit value ([#18561](sveltejs/svelte#18561))
##### [v5.56.7](https://github.com/sveltejs/svelte/blob/HEAD/packages/svelte/CHANGELOG.md#5567)

##### Patch Changes

- chore: provide `indent` option for `print` ([#18474](sveltejs/svelte#18474))
renovate Bot added a commit to cigaleapp/cigale that referenced this pull request Aug 2, 2026
##### [v5.56.8](https://github.com/sveltejs/svelte/blob/HEAD/packages/svelte/CHANGELOG.md#5568)

##### Patch Changes

- fix: call `onerror` and provide a working `reset` when hydrating a failed boundary ([#18556](sveltejs/svelte#18556))

- fix: preserve select selection when spread attributes omit value ([#18561](sveltejs/svelte#18561))
##### [v5.56.7](https://github.com/sveltejs/svelte/blob/HEAD/packages/svelte/CHANGELOG.md#5567)

##### Patch Changes

- chore: provide `indent` option for `print` ([#18474](sveltejs/svelte#18474))
dummdidumm pushed a commit that referenced this pull request Sep 28, 2026
- `destroy_effect` cleared a boundary's component context while retained
reset callbacks and queued error work could still re-enter its run path.
- Reset handling is intentionally shared with hydration by
[#18556](#18556), while `onerror`
is deferred for safe state mutation by
[#17561](#17561); neither path
checked the boundary lifetime.
- Make retained and deferred boundary continuations inert during
destruction instead of masking null contexts in `bind:this` or `#run`,
which would still let dead effects and callbacks restart.
- Cover resets from `onerror` and failed snippets, cleanup ordering,
delayed error transforms, DOM rendering, and hydration.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A <svelte:boundary> hydrated in the failed state can never be reset

2 participants