Skip to content
Changelog

Factory-built and database-stored components get crash-safe background runs

October 16, 20261 min read

Agno's AgentOS stamps a resolution scope on the durable ticket for an AgentFactory, TeamFactory or WorkflowFactory, a component created through the components API, or a version-pinned run, and the queue worker replays that resolution when it claims the job. Earlier releases accepted these runs with a 202, logged that they bypass the durable queue and ran them as a detached task on the accepting replica, so a crash or deploy there lost them.

For a stored component the scope records where it came from, its owner and the version to execute, so a publish after submission leaves the queued run on the version it was accepted with. For a factory the ticket carries the user_id, session_id, validated factory_input, and the trusted claims and scopes the auth middleware set at the door. The worker calls the factory again with that context, and /queue/jobs shows the claims as "[redacted]".

Polling a factory run works now too. Every run read route used to resolve the factory without a request and answer 400; reads go to the factory's run rows and never call the factory. We ran the same AgentFactory on both releases, with an embedded Postgres and a stub model standing in for a real one:

Before
POST /agents/durable-writer/runs  202
WARNING Background run bypasses the durable queue ... NOT durable.
GET run: 400 {"detail": "Request context is required for factory agents"}
 
Now
POST /agents/durable-writer/runs  202
INFO    Agent queued: durable-writer
GET run: 200 {"status": "COMPLETED", "content": "done: Write about job queues."}
GET /queue/jobs/{run_id}: 200 {"pinned": null, "source": "factory", "context": {"input": {"style": "limerick"}, "claims": "[redacted]", "scopes": [], "user_id": "mira", ...}}

The worker has no HTTP request, so ctx.request there is a QueuedRequest. A factory that reads a header from it still gets its 202 at the door, then the run ends ERROR with the fix in its content:

GET run: 200 {"status": "ERROR", "content": "Factory could not build the component: Factory read ctx.request.headers, but queued runs have no HTTP request. Pass what the factory needs through factory_input or the trusted claims."}

Build the component from the context's data fields only. The factory can run more than once for a single run, on any replica, so keep its output a function of the context.

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

Frequently asked questions

Yes, with QueueConfig(durable=True). The accepting endpoint saves the factory's request context on the queue ticket, and whichever worker claims it calls the factory again with that context, so any replica can finish the run.

The run read routes resolved the factory without a request. They now read the run rows through the factory's db and never call the factory, so the poll answers 200 even when the factory has required input fields.

No. On the worker ctx.request is a QueuedRequest, and any attribute read fails the run with an error that names the fix. Pass what the factory needs through factory_input or trusted claims set by middleware.

Agents, teams and workflows registered in code, ones created through the components API, version-pinned runs, and AgentFactory, TeamFactory and WorkflowFactory entries.

Shipped around the same time