Skip to content

RunState pending input can be duplicated when a Session append loses acknowledgement #4775

Description

@FU-max-boop

Please read this first

  • Read the RunState and Session documentation.
  • Searched open issues and pull requests; no duplicate found.

Describe the bug

When a resumed RunState has input staged with RunState.add_input() and uses a client-managed Session, the SDK persists that guarded input before the next model call. If Session.add_items() commits the batch and then raises because the acknowledgement is lost, the RunState has no durable checkpoint for that append.

The input therefore remains pending. Retrying the same state runs the already-passing input guardrail again and appends the same logical input a second time. The behavior reproduces for sync and streaming runs, and after a RunState JSON round trip.

Debug information

  • Agents SDK version: v0.22.0 and main at 89c02c8
  • Python version: 3.13.9
  • Operating system: macOS 15.6.1
  • Model and model provider: deterministic local ScriptedModel; no provider request is needed
  • Does the issue reproduce with the latest Agents SDK release? Yes
  • Does the issue occur consistently or intermittently? Consistently

The raised exception is an intentional RuntimeError from a test Session immediately after it stores the supplied items, modeling a lost acknowledgement.

Repro steps

  1. Produce a resumable RunState and attach a client-managed Session.
  2. Call state.add_input("Late input").
  3. Make Session.add_items() store the batch and then raise.
  4. Retry Runner.run(agent, state, session=session), optionally after state.to_json() / RunState.from_json().
  5. Inspect Session history and input guardrail calls.

Observed result: the Session contains two occurrences of Late input and the same pending-input guardrail runs twice.

The same failure-before-commit case also lacks a resumable write checkpoint, so recovery cannot distinguish it from the lost-ack case.

Expected behavior

The pending input append should use the existing fail-closed Session write reconciliation mechanism. A retry should append once after an atomic failure, recognize an already committed lost-ack append without writing again, consume only the checkpoint-owned pending-input prefix, and not rerun a guardrail that already passed.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions