Skip to content

fix(signals): a write recovers an errored writable memo without a torn read (#4110) - #4167

Merged
ryansolid merged 3 commits into
nextfrom
fix/writable-memo-error-4110
Oct 20, 2026
Merged

ryansolid merged 3 commits into
nextfrom
fix/writable-memo-error-4110

Conversation

@ryansolid

Copy link
Copy Markdown
Member

Summary

Fixes #4110. Supersedes #4143, whose commit by @Neekoras is kept here as-is; the second commit adds the changes from our review.

Before: a writable memo whose derivation threw kept throwing that error after setValue(...). Effects stayed on their error arm and <Errored> never recovered. With #4143 alone the memo recovered, but a read between the write and its flush (or under an action holding the write) returned the old value instead of the error, a torn read.

After: a write to an errored writable memo recovers it when the write commits. Until then the error stays what readers see (D-41), so a read before the flush, or an untracked read mid-action, still throws. A write made after a pass staged an error under the same action now lands as a value. Before this, effects received the written value on their error arm, a case left over from #4148.

How: in setMemo, an errored node records its error as the shown error before setSignal, then clears STATUS_ERROR. The shown error also lets a write equal to the last good value through the equality gate. After setSignal, the write clears CONFIG_STAGED_ERROR, because the staging is now a value.

Size: about +50 B minified on the core floor (+60 B including #4163, which merged without re-recording). Hello world (app: compiled floor) stays under 10 KB brotli at about 9.88 KB. Caps will be raised from this PR's CI numbers with dated ledger notes.

Size-Exception: a write must recover an errored writable memo without a torn read (#4110); the maintainer accepted about +50 B on the core floor on 2026-10-20.

How did you test this change?


Generated by Claude Code

Neekoras and others added 2 commits October 20, 2026 04:15
STATUS_ERROR was only cleared by a recompute or an async settle, neither of which a direct write performs, so an errored memo kept throwing the stale error to every later reader.
…its (#4110)

Builds on #4143. The error stays the display until the write commits, so a
read between the write and its flush, or under the action holding it, still
throws instead of returning the old value. A write also clears an error a
pass staged under the same hold, so effects see the written value instead
of their error arm (the action-write case left from #4148).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018LY7F9czoRqdMEvk2QwPr1
@changeset-bot

changeset-bot Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bdc5d4f

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

This PR includes changesets to release 14 packages
Name Type
@solidjs/signals Patch
test-integration Patch
@solidjs/web Patch
@solidjs/babel-plugin Patch
@solidjs/compiler Patch
@solidjs/diagnostics Patch
@solidjs/element Patch
@solidjs/h Patch
@solidjs/html Patch
solid-js Patch
@solidjs/universal Patch
board-example Patch
server-dbmon-example Patch
todos-server-example 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

@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Size (brotli, eager entry chunk)

scenario head vs base minified vs base minified vs recorded cap lazy chunks (not counted)
signals: core floor (createSignal/Memo/Effect/Root/flush) 7.37 KB +26 B (+0.4%) +50 B 0 B 7.39 KB ✅
signals: + createStore 14.93 KB +22 B (+0.1%) +51 B 0 B 14.95 KB ✅
signals: + isPending/latest 10.14 KB +14 B (+0.1%) +51 B 0 B 10.16 KB ✅
signals: + until/refresh (truth readers, no optimism) 7.97 KB +20 B (+0.3%) +50 B 0 B 7.98 KB ✅
app: render + one signal (the simple-app floor) 9.68 KB +9 B (+0.1%) +50 B 0 B 9.70 KB ✅
app: hydrating (no stores) with Show/For/Loading/Errored/lazy 18.31 KB −8 B (−0.0%) +51 B 0 B 18.32 KB ✅ lazy-page.js 0.04 KB
app: hydrating + every store primitive family 30.31 KB −26 B (−0.1%) +51 B 0 B 30.32 KB ✅ lazy-page.js 0.04 KB
app: CSR with Show/For/Loading/Errored/lazy 13.26 KB +27 B (+0.2%) +51 B 0 B 13.28 KB ✅ lazy-page.js 0.04 KB
app: CSR, observe tier (same app on the observe artifacts) 15.02 KB +24 B (+0.2%) +51 B 0 B 15.03 KB ✅ lazy-page.js 0.04 KB
app: CSR, observe tier + attribution engine enabled 29.61 KB +35 B (+0.1%) +51 B 0 B 29.63 KB ✅ lazy-page.js 0.04 KB
app: compiled floor (one template, one text hole, one delegated click) 9.88 KB +34 B (+0.3%) +50 B 0 B 9.89 KB ✅
app: compiled CSR (JSX todo app: spread/merge/omit, events, class/style, keyed For, Show, Loading + lazy, store) 26.09 KB −7 B (−0.0%) +51 B 0 B 26.10 KB ✅ stats.js 0.18 KB
app: compiled hydrating (the same JSX todo app through hydrate(), compiled hydratable) 32.09 KB −25 B (−0.1%) +51 B 0 B 32.13 KB ✅ stats.js 0.20 KB
frames: eager client consumer (frames client + transport, lazy codec) 11.75 KB 0 B 0 B 0 B 11.76 KB ✅
page: base server components (hydrating + dynamic + frames + sf reference) 33.19 KB +27 B (+0.1%) +51 B 0 B 33.20 KB ✅ assets.js 0.78 KB, decode.js 6.25 KB, lazy-page.js 0.04 KB, regions.js 1.07 KB, trace.js 9.02 KB, wire.js 2.78 KB
page: live server components (base + live/GET + action + isPending/latest) 37.47 KB +3 B (+0.0%) +51 B 0 B 37.48 KB ✅ assets.js 0.78 KB, decode.js 6.25 KB, lazy-page.js 0.04 KB, regions.js 1.06 KB, trace.js 9.06 KB, wire.js 2.78 KB
page: compiled base server components (the base page as JSX: templates with class/style/attributes/events, For/Show; no spread) 35.21 KB +1 B (+0.0%) +51 B 0 B 35.23 KB ✅ assets.js 0.78 KB, decode.js 6.25 KB, regions.js 1.06 KB, sc-comments.js 0.20 KB, trace.js 9.06 KB, wire.js 2.77 KB
page: compiled live server components (the compiled base page + live/GET + action + isPending/latest) 41.30 KB +45 B (+0.1%) +51 B 0 B 41.31 KB ✅ eager (counted): web.js 21.93 KB; assets.js 0.78 KB, decode.js 6.25 KB, regions.js 1.06 KB, sc-comments.js 0.19 KB, trace.js 9.08 KB, wire.js 2.77 KB
page: base + router (base page + @solidjs/router: createRouter, two routes, preload, useNavigate) 40.71 KB +69 B (+0.2%) +51 B 0 B 40.72 KB ✅ assets.js 0.78 KB, decode.js 6.25 KB, lazy-page.js 0.04 KB, regions.js 1.06 KB, server.js 1.02 KB, serverForms.js 3.59 KB, trace.js 9.13 KB, wire.js 2.77 KB
page: live + router (live page + @solidjs/router: createRouter, two routes, preload, useNavigate) 46.90 KB +41 B (+0.1%) +51 B 0 B 46.91 KB ✅ eager (counted): client.js 27.09 KB; assets.js 0.78 KB, decode.js 6.25 KB, lazy-page.js 0.04 KB, regions.js 1.07 KB, server.js 1.02 KB, serverForms.js 3.30 KB, trace.js 9.06 KB, wire.js 2.80 KB
server: floor (getRequestEvent + isServer) 1.33 KB 0 B 0 B 0 B 1.34 KB ✅
server: renderToString (the server-render floor) 20.62 KB 0 B 0 B −30 B 20.59 KB ⚠️ over by 31 B, 50 B minified headroom

⚠️ Over the brotli cap within the minified allowance (passes)

  • server: renderToString (the server-render floor): over brotli cap by 31 B; minified 72,124 B vs 72,154 B recorded with the cap (−30 B) — 50 B of the 20 B minified allowance left; +0 B minified over this PR's base

Bundled with Rolldown (what Vite ships), brotli q11, decimal KB. A scenario fails only when it is over its brotli cap and its minified size is more than 20 B over the minified recorded with the cap; over the cap within that allowance is brotli layout noise and passes with a warning. Caps and their recorded minified in scripts/size/scenarios.js; the floor and page caps in floor-caps.json are frozen (lower only, or Size-Exception: in the PR body). npm run ratchet lowers caps per RC; it never raises one (scripts/size/README.md).

Accepted by the maintainer on 2026-10-20: about +50 B minified on the core
floor. Also re-records #4163's +9 B, which merged without a re-record.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018LY7F9czoRqdMEvk2QwPr1
@coveralls

coveralls commented Oct 20, 2026 •

Copy link
Copy Markdown

Coverage Report for CI Build 39369656537

Coverage remained the same at 77.555%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 1347
Covered Lines: 1103
Line Coverage: 81.89%
Relevant Branches: 1050
Covered Branches: 756
Branch Coverage: 72.0%
Branches in Coverage %: Yes
Coverage Strength: 27.54 hits per line

💛 - Coveralls

@ryansolid
ryansolid merged commit 71153bf into next Oct 20, 2026
7 checks passed
@codspeed

codspeed Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Merging this PR will improve performance by 11.9%

⚠️ 3 benchmarks spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
✅ 195 untouched benchmarks
⏩ 5 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
⚡ build + consume 777 µs 694.3 µs +11.9%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing fix/writable-memo-error-4110 (cf4e5c8) with next (7014aca)

Open in CodSpeed

Footnotes

  1. 5 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

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.

4 participants