Skip to content
Changelog

Background runs on one session now execute one at a time, in the order you sent them

October 16, 20262 min read

With QueueConfig(durable=True), Agno's AgentOS stamps every background ticket with a session_seq, and a worker claims it only once no earlier run in the same session is still queued, running or paused. The rule covers agents, teams and workflows alike.

Picture a chat client that lets someone send a second message before the first answer arrives. Earlier releases ran both turns side by side: the second loaded its history before the first had written anything, session state went to whichever run saved last, and answers could land out of order.

A waiting run stays queued and spends no attempt and no worker slot. Runs on other sessions keep executing concurrently, up to max_concurrency. A run paused for human approval holds its session until someone continues or cancels it, since the runs behind it probably depend on its outcome, and a continued or retried run keeps its place in line.

We submitted three background runs to one session and one to another on an embedded Postgres, with a stub model that waits one second standing in for a real one.

Real output timeline of four background runs, each model call one second long. Before, on earlier releases, runs A, B and C on session chat-1 and run D on chat-2 all start together at about one second. Now, runs A and D start together right away, run B on chat-1 starts after A ends, and run C starts after B ends. A poll of run B while run A is running returns status PENDING with waiting_on set to run A's id.

The setting is on by default:

from agno.os import AgentOS, QueueConfig
 
agent_os = AgentOS(
    agents=[agent],
    db=db,
    queue=QueueConfig(durable=True),  # queue_per_session=True is the default
)

GET /config reports what the queue actually enforces, so a client can discover it without admin access:

GET /config queue: {"durable": true, "queue_per_session": true}

Pass queue_per_session=False to let runs on one session execute concurrently again. Claude, Codex and the other external agents stay serialized either way, because their sessions refuse a second concurrent turn. A custom queue store opts in by setting serializes_sessions = True; without it, the worker logs that marked tickets run concurrently and /config reports queue_per_session: false.

The line only covers runs that go through the durable queue. Media uploads, non-JSON payloads and direct non-background calls run outside it, so keep them off sessions that also take queued runs.

See the cookbook, and learn more about the durable queue in the documentation.

Frequently asked questions

With QueueConfig(durable=True), AgentOS gives each ticket a place in its session's line and claims it only when no earlier run in that session is queued, running or paused. Runs on other sessions keep running concurrently up to max_concurrency.

Poll the run. While it waits behind another run in its session, the response carries waiting_on with that run's id, and no admin access to the queue is needed.

Pass QueueConfig(durable=True, queue_per_session=False). Claude, Codex and the other external agents stay serialized either way, because their sessions refuse a second concurrent turn.

Upgrade every replica together and drain the queue first. Tickets accepted before the upgrade, or while the setting was off, are claimed without the session check.

Shipped around the same time