Skip to content

New async memo reveals a held signal update early #3800

Description

@GabbeV

Describe the bug

A newly created async memo reads an update that is still held by an existing async derivation, then notifies a scheduled render effect before that update is revealed. The existing scheduled render effect still last reported count = 1, slow = 1, while the new one reports 2.

The same newly created memo stays held when its first result is synchronous. Returning Promise.resolve(count()) instead makes it reveal early.

Your Example Website or App

https://s.olid.uk/id/22KYZfNOSnqqlHlUzL1MqQ

Steps to Reproduce the Bug or Issue

  1. Open the playground console and press Build (or reload).
  2. After about one second, observe parent 1 1.
  3. About 100ms later, observe child 2 while the parent is still held at 1 1.
  4. Roughly one second after that, the slow derivation settles and the parent finally reports parent 2 2.

Expected behavior

The new scheduled render observer should stay held until the input update can be revealed with its slow derivation. An asynchronous first result should preserve the inherited hold, just as a synchronous first result does.

Screenshots or Videos

No response

Platform

  • OS: Linux
  • Browser: Chromium 151.0.7922.173
  • Solid: 2.0.0-rc.13 in the playground; also reproduced on upstream next at b0bad02, in development and production.

Additional context

Both render effects use { schedule: true }, so this excludes the immediate mounting-effect path. The repro uses only primitives.

Two controls preserve the hold:

  • Replace Promise.resolve(count()) with count().
  • Remove the explicit flush() before creating the new memo.

The development warnings about unowned effects are expected in this minimal repro.

Related earlier report: #3408. Here the synchronous first result behaves correctly; the asynchronous first result does not.

Activity

  1. ryansolid commented on Oct 7, 2026

    @ryansolid
    Member

    Thanks @GabbeV. The minimal repro and the two controls pointed straight at the async first-result path. Fixed in #3881 (bc51ada).

    A new memo whose first result is async skipped the born-held path that a synchronous first result takes (A29), so its first answer landed outside the transaction and its render effect showed child 2 beside parent 1 1. The memo now records the transaction it was born into, and its first answer is staged into that transaction while it is still live, so parent 2 2 and child 2 reveal together at the commit. The hold still doesn't wait for the new memo: if its first load lands after the hold commits, it is its own commit.

    — Claude via Cursor

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