Skip to content

Fix hydration state restoration for custom element attribute updates - #18467

Merged
dummdidumm merged 3 commits into
sveltejs:mainfrom
fallintoplace:fix/hydration-attributes-restore
Sep 28, 2026
Merged

dummdidumm merged 3 commits into
sveltejs:mainfrom
fallintoplace:fix/hydration-attributes-restore

Conversation

@fallintoplace

@fallintoplace fallintoplace commented Jun 25, 2026 •

Copy link
Copy Markdown
Contributor

Problem

While hydrating custom elements, set_attributes temporarily disables global hydration state via set_hydrating(false) and restores it only at the end. If a prop setter or attribute operation throws partway through, hydration mode can remain disabled globally.

Fix

Wrap the temporary hydration-state override in a try/finally block in set_attributes so set_hydrating(true) always runs when is_hydrating_custom_element is true.

Validation

No runtime tests were run in this PR workflow. This is a control-flow safety fix to ensure state restoration under error conditions.

@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.

what's the use case? please provide some rationale - if something throws we normally should switch to the error boundary etc, so restoring hydration state is irrelevant. also needs a test and changeset

@dummdidumm dummdidumm added the awaiting submitter needs a reproduction, or clarification label Aug 24, 2026
@fallintoplace
fallintoplace force-pushed the fix/hydration-attributes-restore branch from f0bd6da to c0eb1c5 Compare August 24, 2026 14:32
@pkg-svelte-dev

pkg-svelte-dev Bot commented Aug 24, 2026 •

Copy link
Copy Markdown

Install the latest version of svelte from 6903be8:

pnpm add https://pkg.svelte.dev/svelte/c/6903be843fdd6bed54a0632522db0bed5aaecb6d

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

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 Aug 24, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6903be8

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

@fallintoplace

Copy link
Copy Markdown
Contributor Author

The use case is a custom element setter throwing during hydration while the error is caught by a svelte:boundary.
Before this fix, hydration stayed disabled after the setter threw. The fallback rendered, but the original server-rendered custom element was left behind after unmount.

@fallintoplace

Copy link
Copy Markdown
Contributor Author

I checked the git history and I think this came from #15217 (3b7066f).

That change added set_hydrating(false) in both set_custom_element_data and set_attributes. In set_custom_element_data it gets restored in a finally, but in set_attributes it only gets restored at the end.

In this case the setter throws, but svelte:boundary catches the error and keeps going. At that point hydrating is still false, so the hydration cleanup does not happen and the old custom element stays in the DOM.

So I think this PR just makes set_attributes behave the same way as set_custom_element_data when something throws.

@dummdidumm
dummdidumm self-requested a review September 28, 2026 21:09

@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 020242d into sveltejs:main Sep 28, 2026
11 of 12 checks passed
@github-actions github-actions Bot mentioned this pull request Sep 28, 2026
dummdidumm pushed a commit that referenced this pull request Oct 6, 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.57.2

### Patch Changes

- fix: don't create async deriveds for a component destroyed while
waiting for its top-level `await`
([#18931](#18931))

- fix: prevent untracked derived reads from retaining disconnected
dependencies ([#18829](#18829))

- fix: error if `{:else}` is followed by another `{:else}` or `{:else if
...}` ([#18900](#18900))

- fix: avoid tracking SvelteURLSearchParams during URL updates
([#18791](#18791))

- fix: restore hydration state when custom element attribute updates
throw ([#18467](#18467))

- fix: attach event handlers that are assigned after a top-level `await`
in production ([#18933](#18933))

- fix: flush anything pending before invoking flushSync callback
function ([#18878](#18878))

- fix: preserve location information for `await` wrappers
([#18859](#18859))

- fix: keep a reactive assignment with no dependencies when `migrate`
cannot turn it into a declaration
([#18817](#18817))

- fix: don't hang `migrate` on a declaration that shares a line with its
script tag ([#18816](#18816))

- fix: prevent duplicate subscriptions when reconnecting deriveds
([#18899](#18899))

- fix: recover when an unexpected node replaces a nested boundary
comment during hydration
([#18925](#18925))

- fix: print `{#await}` blocks that only have a pending branch
([#18908](#18908))

- fix: don't warn about a redundant `link` role on `<area>` elements
without an `href`
([#18872](#18872))

- fix: don't throw `state_unsafe_mutation` when the focused element is
removed while `bind:activeElement` is used
([#18921](#18921))

- fix: don't overwrite an unchanged spread `value`, preserving
incomplete number input
([#18864](#18864))

- fix: read batch-local array on each-block commit
([#18879](#18879))

- fix: error at compile time when a declaration in a snippet redeclares
one of its parameters
([#18842](#18842))

- fix: error when using `let:` directives on a component with a
`children` snippet
([#18873](#18873))

- fix: ignore stale boundary reset callbacks
([#18886](#18886))

- fix: handle deferred transitions aborted before initialization
([#18776](#18776))

- fix: prevent hydration mismatch recovery from being intercepted by
error boundaries
([#18841](#18841))

- fix: preserve dynamic element connections during hydration
([#18855](#18855))

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
dadezzz pushed a commit to dadezzz/events-cash-register that referenced this pull request Oct 10, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [svelte](https://svelte.dev) ([source](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte)) | [`5.57.1` → `5.57.2`](https://renovatebot.com/diffs/npm/svelte/5.57.1/5.57.2) | ![age](https://developer.mend.io/api/mc/badges/age/npm/svelte/5.57.2?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/svelte/5.57.1/5.57.2?slim=true) |

---

### Release Notes

<details>
<summary>sveltejs/svelte (svelte)</summary>

### [`v5.57.2`](https://github.com/sveltejs/svelte/blob/HEAD/packages/svelte/CHANGELOG.md#5572)

[Compare Source](https://github.com/sveltejs/svelte/compare/svelte@5.57.1...svelte@5.57.2)

##### Patch Changes

- fix: don't create async deriveds for a component destroyed while waiting for its top-level `await` ([#&#8203;18931](sveltejs/svelte#18931))

- fix: prevent untracked derived reads from retaining disconnected dependencies ([#&#8203;18829](sveltejs/svelte#18829))

- fix: error if `{:else}` is followed by another `{:else}` or `{:else if ...}` ([#&#8203;18900](sveltejs/svelte#18900))

- fix: avoid tracking SvelteURLSearchParams during URL updates ([#&#8203;18791](sveltejs/svelte#18791))

- fix: restore hydration state when custom element attribute updates throw ([#&#8203;18467](sveltejs/svelte#18467))

- fix: attach event handlers that are assigned after a top-level `await` in production ([#&#8203;18933](sveltejs/svelte#18933))

- fix: flush anything pending before invoking flushSync callback function ([#&#8203;18878](sveltejs/svelte#18878))

- fix: preserve location information for `await` wrappers ([#&#8203;18859](sveltejs/svelte#18859))

- fix: keep a reactive assignment with no dependencies when `migrate` cannot turn it into a declaration ([#&#8203;18817](sveltejs/svelte#18817))

- fix: don't hang `migrate` on a declaration that shares a line with its script tag ([#&#8203;18816](sveltejs/svelte#18816))

- fix: prevent duplicate subscriptions when reconnecting deriveds ([#&#8203;18899](sveltejs/svelte#18899))

- fix: recover when an unexpected node replaces a nested boundary comment during hydration ([#&#8203;18925](sveltejs/svelte#18925))

- fix: print `{#await}` blocks that only have a pending branch ([#&#8203;18908](sveltejs/svelte#18908))

- fix: don't warn about a redundant `link` role on `<area>` elements without an `href` ([#&#8203;18872](sveltejs/svelte#18872))

- fix: don't throw `state_unsafe_mutation` when the focused element is removed while `bind:activeElement` is used ([#&#8203;18921](sveltejs/svelte#18921))

- fix: don't overwrite an unchanged spread `value`, preserving incomplete number input ([#&#8203;18864](sveltejs/svelte#18864))

- fix: read batch-local array on each-block commit ([#&#8203;18879](sveltejs/svelte#18879))

- fix: error at compile time when a declaration in a snippet redeclares one of its parameters ([#&#8203;18842](sveltejs/svelte#18842))

- fix: error when using `let:` directives on a component with a `children` snippet ([#&#8203;18873](sveltejs/svelte#18873))

- fix: ignore stale boundary reset callbacks ([#&#8203;18886](sveltejs/svelte#18886))

- fix: handle deferred transitions aborted before initialization ([#&#8203;18776](sveltejs/svelte#18776))

- fix: prevent hydration mismatch recovery from being intercepted by error boundaries ([#&#8203;18841](sveltejs/svelte#18841))

- fix: preserve dynamic element connections during hydration ([#&#8203;18855](sveltejs/svelte#18855))

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xNDguNCIsInVwZGF0ZWRJblZlciI6IjQ0LjE0OC40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting submitter needs a reproduction, or clarification

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants