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.
Installs into .claude/skills of the current project.
Are you the author of Iii Error Handling?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/iii-hq-iii-error-handling)
---
name: iii-error-handling
description: >-
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.
---
# 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.