Skip to content

2.0.0-rc.0 | royal flush - inconsistent read/write in onSettled #3006

Closed

Description

@mizulu

Describe the bug

writing to a signal in an onSettled callback
becomes the signal value on the same tick

if this is by design it should be documented

Your Example Website or App

https://s.olid.uk/id/OgFbvGsbQ2qShGPjTe6LEw

Steps to Reproduce the Bug or Issue

see playground

Expected behavior

write / read in onSettled should be similar to write/read in other unowned scopes.
unless it is by design then we need stronger docs about this.

Screenshots or Videos

No response

Platform

.

Additional context

additionally onSettled does not support flush() calls and should be documented

Activity

  1. ryansolid commented on Aug 17, 2026

    @ryansolid
    Member

    Thanks for the careful report — you found a genuine inconsistency, and it's now fixed on next (a7780c7).

    What was happening: onSettled (and createTrackedEffect) callbacks ran with a tracked context internally, which made them observe their own staged writes immediately — unlike the effect half of createEffect and event handlers, which read committed values until the graph settles. That asymmetry was an accident of the read path, not a design choice.

    The fix: all effect-phase scopes now share one contract:

    • Reads return the settled graph — a callback never observes its own unsettled write (same as setState in a React effect).
    • Writes are queued into the same flush's continuation, so dependent memos/effects still update before the flush returns — nothing is lost or deferred to another tick.
    • Functional setters still compose: set(v => v + 1) twice increments twice.

    On flush(): it genuinely cannot run inside these callbacks — the flush that invokes them is already in progress, so it's inherently non-reentrant (React's flushSync has the identical restriction in effects). Dev now reports this loudly everywhere (onSettled/createTrackedEffect throw; createEffect callbacks warn instead of silently no-op'ing), and both messages point at the escape hatch: queueMicrotask(() => flush()). The JSDoc for onSettled and createTrackedEffect now documents all of this.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions