Skills DirectorySearch all skills ↗

AI agent skill · by @iii-hq

Iii Error Handling

Handle iii engine and SDK errors across Node, Python, Rust, and browser workers. Use when interpreting error codes, retryability, RBAC denial, timeouts, handler failures, or SDK-specific exception surfaces.

Archived 2026-10-23 · Source updated 2026-10-01. Check the original repository for the latest version.

SKILL.md instructions

# Error Handling

iii has two broad error classes: SDK/local errors and engine/remote invocation errors. Agents should branch on the error code instead of matching only message strings.

## Error Codes

Branch on exact `code` strings, but keep engine wire codes separate from SDK-local codes.

| Code | Emitted by | Meaning | Typical handling |
| --- | --- | --- | --- |
| `function_not_found` | Engine and SDK local dispatch | No registered function is available under that ID | Check function ID, worker install/startup, discovery, and trigger type hints |
| `invocation_error` | Engine invocation/router path | Engine failed to route, record, or complete the invocation | Inspect engine logs, protocol state, and worker connectivity |
| `invocation_stopped` | Engine invocation handler | Invocation was cancelled or stopped by the engine/runtime | Treat as failed work; decide whether caller should retry |
| `FORBIDDEN` | RBAC / worker-gated engine functions | RBAC denied the action | Do not retry blindly; inspect policy, auth context, and allowed functions |
| `timeout` | A target worker's handler, forwarded verbatim by the engine | Not produced by the engine or the Node/Python SDKs (they emit `TIMEOUT`); appears only if the worker you called returns it | Treat as a timeout if you know the target worker emits it; otherwise branch on `TIMEOUT` |
| `function_not_invokable` | SDK local dispatch | Registration exists but cannot be invoked as a normal local function | Inspect registration/invocation type |
| `invocation_failed` | SDK worker handler wrappers | Local worker handler, HTTP-invoked function wrapper, or SDK-side handler path failed | Inspect handler logs, stacktrace, and payload validation |
| `TIMEOUT` | Node/Python SDK caller timeout | Client waited longer than `trigger()` timeout | Increase timeout only if the workload is expected to run long; otherwise optimize or enqueue |

## Handler vs Engine Errors

- Handler errors originate in user function code, SDK local dispatch, or HTTP-invoked endpoints.
- Engine errors originate in routing, invocation state, RBAC, protocol handling, or worker-reported wire errors.
- Queue retries only apply to enqueued work. Synchronous failures are returned directly to the caller.
- Void dispatch does not return handler results, so use logs/observability for failures.

## Retryability

- Retry transient `TIMEOUT` (or a lowercase `timeout` returned by a target worker), transport, or worker reconnect failures only when the operation is idempotent.
- Do not retry `FORBIDDEN` without changing auth/policy.
- Do not retry `function_not_found` by calling the same ID repeatedly; discover functions or install/start the missing worker.
- For reliable background work, use `TriggerAction.Enqueue({ queue })` and queue retry/DLQ policy.

## SDK Surfaces

### Node

```typescript
import { InvocationError } from 'iii-sdk'

try {
  await iii.trigger({ function_id: 'orders::charge', payload })
} catch (error) {
  if (error instanceof InvocationError && error.code === 'FORBIDDEN') {
    throw new Error('Policy denied orders::charge')
  }
  throw error
}
```

### Python

```python
from iii import InvocationError

try:
    result = iii.trigger({"function_id": "orders::charge", "payload": payload})
except InvocationError as exc:
    if exc.code == "FORBIDDEN":
        raise RuntimeError("Policy denied orders::charge")
    if exc.code in ("TIMEOUT", "timeout"):  # SDK caller timeout, or a lowercase code returned by the target worker
        raise RuntimeError("orders::charge timed out")
    raise RuntimeError(f"{exc.code}: {exc.message}")
```

### Rust

```rust
match iii.trigger(request).await {
    Ok(value) => value,
    Err(iii_sdk::Error::Timeout) => {
        return Err("orders::charge timed out".into());
    }
    Err(iii_sdk::Error::Remote { code, message, .. }) if code == "FORBIDDEN" => {
        return Err(format!("policy denied: {message}").into());
    }
    Err(err) => return Err(err.into()),
}
```

### Browser

Browser trigger calls reject with JavaScript errors. Preserve the engine-provided code/message when present and show policy failures as permission errors in UI.

## Pattern Boundaries

- For invocation modes and enqueue decisions, prefer `iii-core-primitives`.
- For SDK-specific exception classes and syntax, prefer `iii-sdk-reference`.
- For workflow-level retry and DLQ design, prefer `iii-architecture-patterns`.
- For RBAC policy design see the [rbac-proxy worker](https://workers.iii.dev/workers/rbac-proxy); for logs and traces around worker failures, use the matching worker page on https://workers.iii.dev/.

## When to Use

- Use this skill when the task mentions iii errors, exception handling, failed invocations, timeouts, forbidden calls, retry behavior, or SDK error classes.

## Boundaries

- Do not retry non-idempotent work automatically unless it is enqueued under queue policy.
- Do not treat RBAC denial as a missing worker.
- Do not generate removed service APIs or adapter-extension APIs.

Related skills