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

# Error Codes

> Stable codes raised by the CLI, the SDK, and tools

Silo has two separate error vocabularies, and they are used in different places.

**`SiloError`** — raised by the CLI and SDK when you are *authoring* an environment. These are your mistakes, not an agent's.

**`ToolError`** — returned to an agent when a tool call fails at *runtime*. These are values, not exceptions, and are written to be read by a model.

## SiloError

```typescript theme={null}
import { SiloError, isSiloError } from "@burn0/silo";

try {
  await env.tasks.add(task);
} catch (error) {
  if (isSiloError(error) && error.code === "task_already_exists") {
    await env.tasks.update(task);
  }
}
```

The CLI prints the same codes alongside its messages and exits non-zero:

```
error: verifier_already_exists: "verifiers/VER-007.ts" already exists in environment "demo".
```

### Environments

| Code                             | Meaning                                                                   |
| -------------------------------- | ------------------------------------------------------------------------- |
| `environment_not_found`          | No environment by that name, or the project directory does not exist      |
| `environment_already_exists`     | An environment with that name is already there                            |
| `template_not_found`             | Unknown `--template` value                                                |
| `entrypoint_outside_environment` | The manifest's `entrypoint` points outside the environment directory      |
| `runtime_contract_invalid`       | The entrypoint does not export `createState`, `bindTools` and `verifiers` |

<Note>
  `runtime_contract_invalid` is often a module-system problem rather than a missing export. An environment in a project without `"type": "module"` loads as CommonJS, and its exports end up nested under `.default`.
</Note>

### Data

| Code                  | Meaning                                            |
| --------------------- | -------------------------------------------------- |
| `data_not_found`      | No dataset by that name                            |
| `data_already_exists` | `data add` on a name already in use — use `update` |
| `invalid_json`        | The file or `--data` payload is not valid JSON     |

### Tasks

| Code                  | Meaning                              |
| --------------------- | ------------------------------------ |
| `task_not_found`      | No task by that id                   |
| `task_already_exists` | `task add` on an id already in use   |
| `task_invalid`        | The task is missing a required field |

### Code resources

| Code                      | Meaning                                                     |
| ------------------------- | ----------------------------------------------------------- |
| `tool_already_exists`     | A tool file by that name is already there                   |
| `verifier_already_exists` | A verifier file by that id is already there                 |
| `registry_not_found`      | The barrel file has no array to register into               |
| `already_registered`      | That identifier is already in the barrel                    |
| `state_type_not_found`    | No state type could be detected — pass `--state <TypeName>` |

### Names

| Code          | Meaning                                                        |
| ------------- | -------------------------------------------------------------- |
| `unsafe_name` | A name that would escape its directory or produce invalid code |

Names are validated because they are interpolated into file paths and generated TypeScript.

## ToolError

Returned to the agent as a value with `isError: true` — never thrown into it. A rejected call is an observation the agent can act on, not the end of the rollout.

```json theme={null}
{
  "output": {
    "code": "invalid_input",
    "message": "Invalid arguments for \"get_opportunity\": \"opportunityId\" is required; \"opportunityID\" is not a parameter; expected one of: opportunityId."
  },
  "isError": true
}
```

| Code             | Use for                                              |
| ---------------- | ---------------------------------------------------- |
| `invalid_input`  | Arguments structurally fine but wrong                |
| `not_found`      | A referenced entity does not exist                   |
| `invalid_state`  | The operation is not legal right now                 |
| `not_allowed`    | The actor may not do this                            |
| `conflict`       | The change contradicts the current world             |
| `limit_exceeded` | A quota or bound was passed                          |
| `tool_not_found` | No tool by that name — raised by Silo, not your code |

Any string is accepted, so a domain can add its own vocabulary. Prefer these when they fit.

```typescript theme={null}
import { toolError } from "@burn0/silo";

throw toolError("not_allowed", `Agent ${agent.name} is not available to take work.`);
```

Inside a tool you `throw`; Silo catches it and returns it to the agent as a value.

### Two are generated by Silo

`invalid_input` for a schema violation and `tool_not_found` for an unknown name are produced before your `run` executes. The schema message names the real parameters, which is what gives a model a chance to self-correct.

## Writing messages an agent can act on

Tool error messages are read by a model mid-run, so they are part of your environment's interface:

```typescript theme={null}
// Weak — the agent learns nothing
throw toolError("invalid_state", "Cannot assign.");

// Useful — names the rule and the current state
throw toolError("invalid_state", `Ticket ${ticket.id} is ${ticket.status}, not open.`);
```

Name what was wrong and what is true instead. If an agent repeatedly makes the same mistake, the message is usually the thing to fix.
