# RunStatus

> Tracon documentation. Tracon is a .NET package family that adds a control
> plane on Microsoft Agent Framework, published as NuGet packages that run
> inside your own ASP.NET Core application.
>
> Page: https://tracon.dev/api/tracon.runstatus/
> Summary: RunStatus: Status of a run.

<div class="api-provenance">Namespace Tracon · Assembly <code>Tracon.Abstractions.dll</code></div>

Status of a run.

```csharp
[JsonConverter(typeof(JsonStringEnumConverter<RunStatus>))]
public enum RunStatus
```

## Fields

`AwaitingApproval = 6` 

The run waits for an operator to decide on a tool call and cannot advance
without that decision.

Seen on any *root* (`Depth == 0`) agent run, whatever started it
— the management API, an OpenAI-compatible endpoint, MCP, A2A, or the queue
(`Prefer: respond-async`). The first version covered only the queue path: the synchronous
paths did not reflect this status at all, and a tool call waiting for
approval silently looked [RunStatus.Completed](/api/tracon.runstatus/). However the decision
arrives — `POST /api/approvals/{id}/decide` for the queue, the caller's
own next turn for synchronous callers — the status label follows the same
principle.

It follows the SAME principle as [RunStatus.AwaitingInput](/api/tracon.runstatus/): an answered run
*stays* in this status, because rewriting history would break the
append-only rule of the event stream. On the queue path the decision
is made through `POST /api/approvals/{id}/decide`, which enqueues a
**new** run (same `sessionId`, new `RunId`).

The value was appended at the end for the same reason as
[RunStatus.AwaitingInput](/api/tracon.runstatus/).

`AwaitingInput = 4` 

The run waits for human input and cannot advance without it.

Seen on [RunKind.Workflow](/api/tracon.runkind/) rows, and on the root agent run of a tool call an
MCP server suspended to ask the user for input (MCP elicitation). When a workflow reaches an
*external request port* — Magentic plan approval, for example —
execution stops, its state is written to a checkpoint and the stream closes.
The answer arrives through `POST /api/workflows/runs/{runId}/respond`,
which opens a **new** run that resumes from the checkpoint.

On an agent run the request is the next turn's input, the same as an approval: the caller
answers in the `inputResponses` of the next run request of the same session, and the
suspended tool call runs again from its start with the answer. Only a run started through the
management API's run endpoint can wait this way; on every other surface (an OpenAI-compatible
call, A2A, an MCP tool call, a queued run) and below the root of an agent tree the server's
request is declined.

The value was appended at the end: statuses are stored as `smallint` in
the database and shifting the existing values would misread old rows. An
answered run *stays* `AwaitingInput`; rewriting history would
break the append-only rule of the event stream. The continued work
shows up in the new run row.

`Canceled = 3` 

The run was cancelled.

`Completed = 1` 

The run finished successfully.

`Failed = 2` 

The run ended with an error.

`Queued = 5` 

The run has not started its work: it waits for a worker, or for a concurrency slot.

Seen for runs started with `Prefer: respond-async`. The
row is written as `Queued` at enqueue time; when the worker actually
runs the job the same id is written again and moves to [RunStatus.Running](/api/tracon.runstatus/).

Seen too for a workflow run that waits, in the process that runs it, for a slot of
`Tracon:Workflows:MaxConcurrentRuns` or of `MaxConcurrentRunsPerTenant`. No job
holds that run; the process does, and writes a heartbeat for it, so run reconciliation closes
the row if the process is gone. The row moves to [RunStatus.Running](/api/tracon.runstatus/) when the run has its slots.

The value was appended at the end for the same reason as [RunStatus.AwaitingInput](/api/tracon.runstatus/).

`Running = 0` 

The run is in progress.

## Remarks

Written to JSON **by name** (`"Code"`), not by number. The
wire contract explains itself that way and survives a change in value order.
The converter sits on the type, so the format is the same everywhere without
touching the consumer's application-wide JSON options. No enum is PERSISTED as
JSON (RunStatus and RunEventType are smallint in the database,
AgentDefinitionOrigin is rebuilt on read), so a format change does not affect
stored data.
