turn/steer can succeed without persisting input while sampling is in flight

Open 💬 0 comments Opened Aug 26, 2026 by angelsbrood

What issue are you seeing?

turn/steer can return a successful response for the active turn while the submitted input remains only in the pending-input queue. If the current sampling request does not complete, Codex emits neither the correlated steered-user item/started nor item/completed lifecycle before the run is stopped.

I observed this through app-server v2 with rust-v0.148.0 (3ba0f711642a888aec92a611a3f3b2211157ff89) on macOS arm64. Source inspection shows the relevant acceptance and sampling ordering is unchanged in the latest rust-v0.149.1 release and current main (3e4707b34b16e139fcb7ad11ab8445993b62bba1).

In a bounded reproduction:

  • turn/start entered a regular active turn.
  • Exactly one turn/steer request was sent with expectedTurnId and clientUserMessageId.
  • The server returned success with the expected active turnId.
  • The active sampling request remained in flight for more than six minutes.
  • No correlated steered-user item/started or item/completed was emitted before the run was stopped.
  • The client therefore could not distinguish durable acceptance from volatile queueing and treated the result as ambiguous/no-retry.

No raw prompt, model output, credential, workspace path, or identifier is included in this report.

What steps can reproduce the bug?

A deterministic model-server test can reproduce this without relying on provider timing:

  1. Start app-server and a regular turn against a controlled model server.
  2. Hold the turn's first sampling response open.
  3. While that sampling request is in flight, send one turn/steer request with fixed expectedTurnId and clientUserMessageId values.
  4. Observe that turn/steer returns the active turnId successfully.
  5. Keep the first sample blocked and observe that no correlated user-message lifecycle is persisted or emitted.
  6. Release the sample and verify the steer is eventually observed exactly once.

The current source ordering explains the behavior:

This permits a successful response with no durable user-item lifecycle for as long as the current sampling request remains in flight.

What is the expected behavior?

An accepted steer should not remain indefinitely volatile behind one long-running sampling request. A successful turn/steer response should provide a durable, reconcilable acceptance boundary. For example, Codex could either:

  1. return success only after the steered input is durably recorded and its correlated lifecycle can be observed; or
  2. durably expose a queued-steer state keyed by clientUserMessageId, while ensuring the active turn reaches a safe steer-processing boundary without waiting indefinitely for the current provider request.

The fix should avoid cancelling tool side effects, duplicating delivery, or manufacturing a user-item lifecycle before persistence.

Additional information

Related but distinct work:

  • #32254 requests durable/idempotent acceptance keyed by clientUserMessageId.
  • #30341 preserves a steer accepted during turn finalization.
  • #29582 concerns model semantics after pending steer input is drained.

This report is specifically the pre-persistence liveness case: the RPC succeeds, but a long in-flight sample prevents the accepted input from reaching its correlated durable lifecycle.

A regression should also cover interrupt, finalization, retry with the same ID and payload, and conflicting reuse of the same ID with different input. I would be happy to prepare a focused patch if maintainers agree on the intended durable-acceptance contract and safe sampling boundary.

View original on GitHub ↗