3.2

Actor and its seven states

An actor is always in one of seven states, and that state decides what a request, a resume, and a suspend do.

A request to your actor returns 503. kubectl get pods shows every pod as Running, and the worker pod log does not name your actor. Kubernetes cannot tell you more, because an actor is not a pod. Its state is a row in Postgres, and kubectl ate get actors reads that row.

When you finish this section, you can read the state of an actor and name the call that moves it. You can also predict what a request does in each state.

The Actor object

Actor: one instance of an ActorTemplate, with a name that is unique in its atespace. An actor is the unit that Substrate suspends and resumes. It can run on a different worker after each resume.

This is the counter actor of the quickstart while it runs.

yamlthe actor my-counter-1 in ACTOR_STATE_RUNNINGFull log
actorTemplate:
  atespace: ate-demo-counter
  name: counter
metadata:
  atespace: ate-demo-counter
  createTime: "2026-11-12T10:13:40.987568669Z"
  name: my-counter-1
  uid: 6f80f612-7547-4f5b-b3ab-831595621806
  updateTime: "2026-11-12T10:30:18.103768100Z"
  version: "16"
status:
  assignedNode: kind-control-plane
  lastAssignedGeneration: 3
  snapshots:
  …
  state: ACTOR_STATE_RUNNING
  workerAssignment:
    nodeName: kind-control-plane
    …
    workerPod: counter-7cc7dcd596-fps97
    …
    workerPodUid: c572e96d-dd9b-4500-b0e3-880a98d9f61d
    workerPool: counter
The snapshot list and the runtime attributes are cut.
FieldWhat it tells you
metadata.versionA counter that rises on every change of the actor. This actor reached 16 in 17 minutes.
status.stateOne of the seven states.
status.workerAssignmentThe worker pod that hosts the sandbox. It is set only while a worker holds the actor.
status.assignedNodeThe node of the worker while the actor runs, and the node that keeps the newest snapshot on its disk after a suspend.
status.snapshotsThe snapshots that the actor can resume from, at most 16.
status.crashA time and a message. It is set when the actor enters ACTOR_STATE_CRASHED, and a revert clears it.

workerAssignment and assignedNode answer different questions. The first says where the actor runs now. The second also says where its last snapshot is after a suspend, and section 3.4 shows how long that lasts.

The seven states

The enum ActorState has seven values at v1.0.0. The state diagram on the architecture page of the project draws four of them, so trust the enum.

StateWhat the actor hasWhat a request does
ACTOR_STATE_SUSPENDEDNo sandbox, and zero or more snapshotsThe router resumes the actor, then the actor answers
ACTOR_STATE_RESUMINGA worker, and a restore that is not completeIt waits for the same resume
ACTOR_STATE_RUNNINGA sandbox on a workerThe actor answers
ACTOR_STATE_SUSPENDINGA worker, and a checkpoint that is not completeA request that is open when the suspend starts gets a 503. We did not test a request that arrives in this state
ACTOR_STATE_CRASHEDNo sandbox, and a crash record503 at once, with the state in the text
ACTOR_STATE_REVERTINGA revert that is not complete503 at once, with the state in the text
ACTOR_STATE_DELETINGA delete that is not complete503 at once, with the state in the text

Three of these states are stable. ACTOR_STATE_SUSPENDED lasts until a request or a resume call arrives. ACTOR_STATE_CRASHED lasts until a revert or a delete, and a request does not end it. ACTOR_STATE_RUNNING lasts until a caller suspends the actor or Substrate marks it crashed. Section 3.3 proves that Substrate does not suspend it. The other four states are steps of one operation.

ACTOR_STATE_RUNNING does not prove that the process is alive. Two actors whose process had exited read ACTOR_STATE_RUNNING for the 20 minutes of our watch. Section 3.5 shows that run.

Old tutorials name a state ACTOR_STATE_PAUSED and a command kubectl ate pause actor. Both are gone at v1.0.0.

Fig. 3.3the seven actor statesstate
The seven actor states and the transitions between them A state figure with the seven states of the ActorState enum at v1.0.0. CreateActor makes an actor in ACTOR_STATE_SUSPENDED. ResumeActor moves it through ACTOR_STATE_RESUMING to ACTOR_STATE_RUNNING. SuspendActor moves it through ACTOR_STATE_SUSPENDING and returns it to ACTOR_STATE_SUSPENDED. A failed restore, a lost worker pod, a memory kill or a failed suspend moves it to ACTOR_STATE_CRASHED, where a request gets 503. The only arrow that leaves ACTOR_STATE_CRASHED is RevertActor, which moves the actor through ACTOR_STATE_REVERTING to ACTOR_STATE_SUSPENDED. DeleteActor moves an actor in every state to ACTOR_STATE_DELETING. In the kagent run, on a second x86 server guest with 16 vCPU and 31 GB, an actor spent a median of 0.38 s in ACTOR_STATE_RESUMING over 98 turns and 0.14 s in ACTOR_STATE_SUSPENDING over 99 suspends. A dotted grey arrow is a transition we read in the code and did not run. ACTOR_STATE_SUSPENDED no sandbox, no worker ACTOR_STATE_RESUMING 0.38 s median of 98 ACTOR_STATE_RUNNING a sandbox on a worker ACTOR_STATE_REVERTING later calls are lost ACTOR_STATE_CRASHED a request gets 503 ACTOR_STATE_SUSPENDING 0.14 s median of 99 CreateActor ResumeActor restored SuspendActor the snapshot is on the node restore failed worker pod gone, memory kill suspend failed RevertActor the only way out last durable snapshot ACTOR_STATE_DELETING then the actor is gone DeleteActor, from every state in our traces: amber is a state change, rose is a failure read in the code, not run: RevertActor on a SUSPENDED actor Not drawn, also code only: RevertActor on a RUNNING actor. Times in RESUMING and SUSPENDING: the kagent run, files-only actors. A process that exits leaves the actor RUNNING. Section 3.5 has that case.
An actor leaves ACTOR_STATE_CRASHED only through RevertActor or DeleteActor, and no request can resume it. Read the top row left to right for a resume, and follow the right edge down for a suspend. The thick arrow is the only one that leaves the rose state. Amber and rose arrows are transitions from our traces, and the dotted grey arrow comes from the code only. The two times are medians from the kagent guest.

The transitions we saw

We drove my-counter-1 through a suspend, a resume, a crash, and a revert in 13 seconds. A script read the state after each step.

outputone actor from suspended to crashed and backFull log
2026-11-12T10:30:09.378Z	state	ACTOR_STATE_SUSPENDED	kind-control-plane	9	SNAPSHOT_OWNER_ACTOR:SNAPSHOT_STORAGE_STATE_COMPLETED:gen3
2026-11-12T10:30:10.310Z	state	ACTOR_STATE_RUNNING	kind-control-plane	11	SNAPSHOT_OWNER_ACTOR:SNAPSHOT_STORAGE_STATE_COMPLETED:gen3
…
2026-11-12T10:30:11.590Z	state-after-crash	ACTOR_STATE_CRASHED	kind-control-plane	12	SNAPSHOT_OWNER_ACTOR:SNAPSHOT_STORAGE_STATE_COMPLETED:gen3
…
2026-11-12T10:30:17.724Z	state	ACTOR_STATE_SUSPENDED	-	14	SNAPSHOT_OWNER_ACTOR:SNAPSHOT_STORAGE_STATE_COMPLETED:gen3
…
2026-11-12T10:30:18.333Z	state	ACTOR_STATE_RUNNING	kind-control-plane	16	SNAPSHOT_OWNER_ACTOR:SNAPSHOT_STORAGE_STATE_COMPLETED:gen3
Five lines of the trace. The columns are the time, the event, the state, the assigned node, the version, and the snapshots.

Look at the version column. A resume moved it from 9 to 11, and the revert moved it from 12 to 14. Each operation writes the actor two times: one write for the step state, and one for the stable state. Our script read the state after each command returned, so it never saw the state between.

The log of ate-api-server does show those states. The listing in section 3.1 has resuming before running, suspending before suspended, and deleting before deleted.

We measured the step states in the kagent run, on a second x86 server guest (16 vCPU, 31 GB). There a harness resumes and suspends an actor for every turn. Over 98 turns, the actor spent a median of 0.38 seconds in ACTOR_STATE_RESUMING. Over 99 suspends, it spent a median of 0.14 seconds in ACTOR_STATE_SUSPENDING. Those actors keep files only, so the times are low. Raw runs

A step state can also last. In our dependency test, one actor was still in ACTOR_STATE_RESUMING five minutes after Postgres came back. Section 8.2 has that case.

Who moves an actor

CallAccepted fromResultWho calls it
CreateActorno actorSUSPENDEDYou, or your harness
ResumeActorSUSPENDED, RESUMING (the call continues the resume), and RUNNING (no change)RESUMING, then RUNNINGThe router for every request, or kubectl ate resume actor
SuspendActorRUNNINGSUSPENDING, then SUSPENDEDYou, or your harness
RevertActorCRASHED, RUNNING, SUSPENDED with a snapshot on the node, and REVERTING (the call continues the revert)REVERTING, then SUSPENDEDYou, or your harness
DeleteActorevery stateDELETING, then no actorYou, or your harness

Substrate itself makes one move: it marks an actor ACTOR_STATE_CRASHED when the sandbox, the worker, or a restore is lost. Section 3.5 lists every way that we reached that state.

A call in the wrong state does not wait. ResumeActor on a crashed actor returns FailedPrecondition at once, and a request gets the same text as a 503. SuspendActor on a suspended actor returns the actor with no change.

The router calls ResumeActor for every request, also when the actor runs. On a running actor the call succeeds, changes nothing, and returns resumed=false. That is why a plain HTTP request is enough to start a suspended actor. Section 1.2 follows one such request.

Our position: read the state before you debug the network. kubectl ate get actors -a <atespace> costs one call. It separates a crashed actor from a full pool and from a dead process, and each of those needs a different fix.

Agent Substrate 1.0.0 · b34fbee · 2026-11-05 · Edition 2026.11