Skip to content

A receive right after all([...]) returns null with a message pending #1021

Closed

Description

@rmcdaniel

Problem

In embedded workflow code, inbox()->receive() / receiveOne() called right after all([...]) returns no message while one is pending on the stream. General's interim review of #1017 found this. The same happens on main before #1017, so it predates that PR.

To investigate

The likely cause is the step the receive sees after all(). A parallel group takes several step positions, and visibleSequence after all() may not be the position the receive is replayed or recorded at. It may also be that the replay guard or recorded-consumption lookup keys on a sequence that the group's children share. Reproduce with a workflow that awaits a signal, runs all([activity(...), activity(...)]), then calls $this->inbox('chat')->receiveOne() with one message pending. Check whether it returns null on the first execution, only on replay, or both.

Expected

A receive after all([...]) behaves like a receive after a single step: on the first execution it consumes the pending message, and on replay it returns that message.

Activity

  1. added
    kind:defectA public product behavior is incorrect
    priority:P2Normal-priority product work
    status:triageAwaiting maintainer classification
    on Nov 4, 2026
  2. rmcdaniel commented on Nov 4, 2026

    @rmcdaniel
    MemberAuthor

    Owner: Workflow. This remains separate from #1024's update-query replay problem; #1030 guard-only work does not fix it. The current MessageStream receive path still derives its visible step and recorded consumption separately from the parallel group's execution span.

    Next is the specified real workflow regression: signal admission, two genuine activities in all(), then receiveOne with one pending message, through first execution and cold replay. Snapshot messages, consumed-step identity, cursor/history and final output before proposing a fix. Repeated/empty receives and legacy consumed history must retain their behavior. I have tied the investigation to the existing issue rather than broadening the rejected cursor-only query prototype. Any global cursor/history semantics change returns to General/Security before implementation.

  3. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Resolved by the shared-cursor correction in #1017, published in 2.20.3. Workflow reproduced the relevant admission timing with a sequential control and genuine parallel activity jobs; a separate parallel-only defect is not established.

    The same six-case test overlay compares three message-admission points (before the signal, before activities, and after activity completion) with sequential versus all() execution. Every case checks two completed activity jobs, a fresh-process query with complete durable-row preservation, and the final completed output.

    • Exact 2.20.2 source b3ba0c73: 6 cases / 37 assertions, two failures, zero errors. A message queued before the signal returns [null, null, null] for first query, fresh-process query and output in both sequential and parallel workflows. The four later-admission cases pass. This isolates the already-known shared cursor hiding an earlier pending stream message, rather than a group-step regression.
    • Current accepted source 98bd9868: all six cases / 37 assertions pass with j1 returned consistently. The first standalone-reader setup error is excluded; its corrected reader boots Testbench directly and performs no migrations or writes. Every owned test container is terminal/no OOM and removed.
    • Independent current Packagist readback identifies 2.20.3 dist reference 4cfa985b0aff1cef9b0f3df518b1d6c20023fa5c. All 689 runtime source files in the downloaded Composer distribution match the tested accepted source, and its manifest's product train is exactly 2.20.3. Downloaded archive SHA-256: 1d5d13f8da7a98f60da1f48a06f8fb678061f4fbf4b505d5495f6d9823b0c277. Existing protected publication/consumer acceptance remains on #1025.

    The regression source is retained in draft #1039. Workflow owns its remaining style/static/normal integration checks. That test-only follow-up adds no runtime patch; the user-visible correction is already delivered in 2.20.3. Historical update-handler query replay remains the separate open #1024.

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

    kind:defectA public product behavior is incorrectpriority:P2Normal-priority product work

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions