> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xpander.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# @on_boot / @on_shutdown

> Run setup and teardown logic around the agent worker.

`@on_boot` registers a function to run **before** the SSE listener subscribes to the platform: useful for warming caches, opening database pools, validating environment, or pre-fetching data. `@on_shutdown` registers a function to run during graceful shutdown.

```python theme={"dark"}
from xpander_sdk import on_boot, on_shutdown

@on_boot
async def warm_caches():
    print("Boot: warming caches…")
    await prefetch_models()

@on_shutdown
async def cleanup():
    print("Shutting down…")
    await close_connections()
```

Both decorators accept either sync or async functions, and you can register multiple handlers: the runtime invokes them in registration order.

## Decorator forms

```python theme={"dark"}
@on_boot
def fn(): ...

@on_boot(configuration=config)
async def fn(): ...
```

```python theme={"dark"}
@on_shutdown
def fn(): ...

@on_shutdown(configuration=config)
async def fn(): ...
```

| Parameter       | Type            | Default | Description                                                                                                            |
| --------------- | --------------- | ------- | ---------------------------------------------------------------------------------------------------------------------- |
| `configuration` | `Configuration` | `None`  | Reserved for future use. Currently only the `Events` module reads config; boot/shutdown handlers receive no arguments. |

## Lifecycle ordering

```text theme={"dark"}
Process start
  ↓
@on_boot handlers (in registration order, await one at a time)
  ↓
Events.start(): subscribes to SSE, begins task dispatch
  ↓
… task dispatch ongoing …
  ↓
SIGINT / SIGTERM received
  ↓
Events.stop(): cancels tracked tasks, closes thread pool
  ↓
@on_shutdown handlers (in registration order, errors logged but don't block)
  ↓
Process exit
```

If a `@on_boot` handler raises, the worker fails to start: the exception propagates and the process exits. If a `@on_shutdown` handler raises, the error is logged but the runtime continues with the next shutdown handler.

## Examples

### Open a DB pool at boot, close on shutdown

```python theme={"dark"}
import asyncpg
from xpander_sdk import on_boot, on_shutdown

pool = None

@on_boot
async def open_pool():
    global pool
    pool = await asyncpg.create_pool(dsn="postgresql://…")

@on_shutdown
async def close_pool():
    if pool:
        await pool.close()
```

### Validate env at boot

```python theme={"dark"}
import os
from xpander_sdk import on_boot

@on_boot
def check_env():
    for var in ("OPENAI_API_KEY", "REDIS_URL"):
        if not os.environ.get(var):
            raise RuntimeError(f"Missing required env var: {var}")
```

Raising in `@on_boot` aborts startup before the worker takes any tasks: useful for fail-fast environment validation.

### Multiple handlers

```python theme={"dark"}
@on_boot
def first():
    print("1")

@on_boot
def second():
    print("2")
```

Both run, in declaration order: `1` then `2`. The runtime awaits each one before moving on.

### Sync and async mix

```python theme={"dark"}
@on_boot
def synchronous():
    print("sync boot")

@on_boot
async def asynchronous():
    print("async boot")
    await asyncio.sleep(0)
```

Sync handlers are called directly; async ones are awaited. Either works.

## Notes

* Boot/shutdown handlers are class-level on `Events`. They register globally per process: there's no scoping to a specific `Events` instance. This is fine for a one-process-one-agent model.
* Shutdown handlers run after the SSE listener is stopped but before final cleanup. By then the process is no longer accepting new tasks; in-flight tasks are cancelled.
