Errors
Handle CrowNestApiError, understand common error codes, and retry safely with idempotency keys.
Every failed API call in the TypeScript SDK throws a CrowNestApiError.
The error carries the HTTP status and a stable, machine-readable code so
you can branch on failures without parsing messages.
CrowNestApiError
CrowNestApiError extends Error with structured fields:
| Property | Type | Description |
|---|---|---|
status | number | HTTP status code. |
code | string | Stable error code in lowercase snake_case, for example quota_exceeded. |
message | string | Human-readable description. |
details | object | undefined | Additional context for some errors. |
import { createCrowNestClient, CrowNestApiError } from "@crownest/sdk";
const client = createCrowNestClient();
try {
await client.sandboxes.create({ ttlMs: 24 * 60 * 60 * 1000 });
} catch (error) {
if (error instanceof CrowNestApiError) {
console.error(error.status, error.code, error.message);
if (error.code === "sandbox_ttl_exceeded") {
// retry with a smaller ttlMs
}
} else {
throw error;
}
}Non-zero exit codes don't throw
A command that runs to completion with a non-zero exit code is a
successful API call. commands.run resolves normally; the SDK only throws
when the API itself fails (for example, the sandbox is destroyed or the
command times out).
const result = await sandbox.commands.run("false");
console.log(result.status); // "exited"
console.log(result.exitCode); // 1 — no error thrownAlways check command.status and command.exitCode to decide whether
the process succeeded.
Common error codes
| Code | HTTP | Meaning |
|---|---|---|
invalid_request | 400 | Malformed request or validation failure. |
sandbox_ttl_exceeded | 400 | Requested ttlMs exceeds your plan limit. |
reserved_env_key | 400 | An env key uses the reserved CROWNEST prefix. |
unauthenticated / invalid_api_key | 401 | Missing, invalid, or revoked credentials. |
billing_required | 402 | Billing configuration required. |
forbidden | 403 | Missing scope or no access to the resource. |
org_suspended | 403 | The organization is suspended. |
not_found | 404 | The resource doesn't exist or isn't visible to you. |
conflict | 409 | State conflict. |
idempotency_key_reused | 409 | The same idempotency key was used with a different request. |
idempotency_request_in_progress | 409 | The original request with this key is still running. |
sandbox_destroyed | 410 | The sandbox is already destroyed. |
file_too_large | 413 | Content exceeds the 256 KB direct read/write limit. |
quota_exceeded | 429 | A plan quota was reached. |
rate_limited | 429 | The request rate limit was reached (120 requests per 60 seconds per API key). |
command_timed_out | 500 | The command exceeded its timeoutMs. |
internal_error / runtime_error | 500 | Server-side failure. |
stream_gap | 500 | Requested log sequence is outside the retention window. |
preview_unavailable | 502 | The preview port isn't responding. |
File operations can also fail with path_outside_workspace,
path_is_directory, path_not_directory, file_not_found,
file_already_exists, parent_directory_not_found,
directory_not_empty, and invalid_file_encoding. See
Files for where each applies.
Retries and idempotency
The create-style operations — sandboxes.create, commands.run,
commands.start, and artifacts.create — accept an idempotencyKey.
The SDK generates one automatically when you don't pass it, so retrying a
failed call with the same key never duplicates work.
Idempotency keys are retained for 24 hours and scoped to the org, credential, method, and route:
- Replaying the same key with the same request returns the original response.
- Reusing the key with a different request fails with
idempotency_key_reused(409). - Retrying while the original request is still running fails with
idempotency_request_in_progress(409).
We recommend this retry policy:
- Retry
429(rate_limited,quota_exceeded) and5xxerrors with exponential backoff. - Pass your own
idempotencyKeywhen you retry create operations across process restarts, so the key survives the restart. - Don't retry
4xxvalidation errors (invalid_request,sandbox_ttl_exceeded,forbidden) — fix the request instead. - Treat
sandbox_destroyed(410) as terminal for that sandbox: create a new one.
Next steps
- Sandboxes — create operations that accept idempotency keys.
- Commands — command status and exit codes.
- API error reference — the REST error envelope.