---
title: "Actor Lifecycle and Supervision"
description: "Follow a Hew actor through spawn, initialization, message handling, graceful stop, cleanup, crash, and supervised restart."
source: "https://hew.sh/docs/concepts/actor-lifecycle"
---

# Actor Lifecycle and Supervision

Actors outlive individual calls, so their construction, cleanup, and recovery are explicit.

A task belongs to the function or scope that started it. An actor has its own lifetime: spawning it creates state and a mailbox, and returning from the spawning function does not by itself stop the actor. This independence is useful for services, caches, and coordinators, but it also means shutdown and recovery need names.

## Follow the normal path

1.  **Construct:** `spawn` evaluates the supplied fields and runs `init` when the actor declares one. No handler observes partly initialized state.
2.  **Start:** one `#[on(start)]` hook may prepare state after construction or restart.
3.  **Handle messages:** each `receive fn` gets exclusive access to the actor's state for its turn, including across suspension.
4.  **Request shutdown:** `stop(actor)` asks for a graceful stop and returns immediately.
5.  **Settle cleanup:** `stopped(actor)` waits until terminal hooks, state cleanup, and owned resource release have finished.

`terminate(actor)` requests cancellation instead of a graceful stop. It also returns immediately. Use it when the operation calls for cancellation, not as a shorter spelling of `stop` followed by `stopped`.

## Start and stop an actor

Lifecycle hooks and terminal cleanup

```hew
actor Reporter {
    var reports: i64 = 0;
 
    #[on(start)]
    fn ready() {
        println("reporter ready");
    }
 
    receive fn record() -> i64 {
        reports += 1;
        reports
    }
 
    #[on(stop)]
    fn finish() {
        println(f"reports written: {reports}");
    }
}
 
fn main() {
    let reporter = spawn Reporter;
    println(reporter.record().expect("reporter available"));
 
    stop(reporter);     // request graceful shutdown
    stopped(reporter);  // wait for finish and state cleanup
}
```

A handler call waits for that handler's turn, not for the actor's whole lifetime. The explicit `stopped` call is the point at which `main` knows terminal cleanup has settled. Calling `stopped` alone can observe a stop requested elsewhere.

### Resources follow ownership

Actor fields can own ordinary values and affine resources. Terminal cleanup releases what the actor still owns after its handlers and scoped tasks have settled. A fault may bypass a normal stop hook, but it does not erase the ownership cleanup obligation. See [resources and linear values](/docs/concepts/ownership#resources) for the two contracts.

## Separate failure from recovery

A fault in B: three recovery strategies

`one_for_one`

1.  A —Continues
2.  B ↻Restarts
3.  C —Continues

`one_for_all`

1.  A ↻Restarts
2.  B ↻Restarts
3.  C ↻Restarts

`rest_for_one`

1.  A —Continues
2.  B ↻Restarts
3.  C ↻Restarts

Three permanent children, declared in order A → B → C. The strategy determines which children restart together.

An actor fault ends the current incarnation. A supervisor owns named child roles and decides whether a failed child should be restarted. Its strategy describes which related children restart, while its intensity clause limits repeated restarts within a time window.

-   `one_for_one` restarts the failed child.
-   `one_for_all` restarts every child.
-   `rest_for_one` restarts the failed child and those declared after it, which can model dependency order.

Each child also has a restart classification: `permanent` restarts after any exit, `transient` after abnormal exit, and `temporary` never restarts. Choose a policy from the service's recovery needs; restart is not a substitute for deciding where durable application data lives.

A supervised role

```hew
actor Worker {
    let id: i64;
    receive fn work(value: i64) -> i64 { value + id }
}
 
supervisor Service {
    strategy: one_for_one;
    intensity: 5 within 10s;
    child primary: Worker { id: 1 } restart: permanent;
}
 
fn main() {
    let service = spawn Service;
    let worker = service.primary;
    println(worker.work(41).expect("worker available"));
 
    stop(service);
    stopped(service);
}
```

## Understand roles and incarnations

A supervised child name such as `service.primary` is a stable role. Calls through the role resolve its current incarnation after a restart. A direct incarnation handle remains tied to the actor instance it named; restarting a role does not turn a stale direct handle into the replacement.

`restarted(role)` waits for an already pending replacement to settle. It does not initiate a restart. A restarted actor is reconstructed from its child declaration and startup path, so state that must survive a crash belongs in another durable component.

**Advanced: runtime states versus source-level lifecycle**

The runtime tracks scheduling states such as idle, runnable, running, suspended, stopping, crashing, crashed, stopped, and sleeping. Most application code should reason through the source operations above: handler completion, `stop`, `terminate`, `stopped`, and supervisor policy. The internal scheduling state is not a public synchronization API.

A request deadline also does not stop an actor. If a request expires before dispatch, the handler does not start. Once claimed, the actor owns the handler turn; a caller that stops waiting does not roll back its effects.

## Continue from here

-   Read the [actor guide's supervision examples](/docs/actors#supervision) for restart strategies and nested trees.
-   Look up exact policies and lifecycle APIs in the [released supervision reference](https://docs.hew.sh/language.supervision.html).
-   Compare the model with Erlang/OTP in the [supervision comparison](/docs/compare/supervision).
-   Use [Structured Concurrency](/docs/concepts/structured-concurrency) for child work whose lifetime belongs to a call or scope.
