Guides / JavaScript & TypeScript / Errors reference
Errors reference
Every error the SDK throws is a Kosa8Error, and an error from the daemon
keeps the daemon's own words: what went wrong, and what to do about it. This page lists each class
with a real message from kosa8 v0.9.10.
The hierarchy
Error
└─ Kosa8Error anything this SDK throws; also client-side checks
├─ Kosa8ConnectionError the daemon could not be reached
├─ Kosa8StreamError a run, exec or attach ended with an error frame
├─ Kosa8CheckpointError checkpoint bookkeeping: none to rewind to, bad name
└─ Kosa8ApiError the daemon answered with an HTTP error
├─ Kosa8BadRequestError 400
├─ Kosa8AuthError 401
├─ Kosa8PolicyError 403
│ └─ Kosa8ApprovalRequiredError 403 with a signed-approval refusal
├─ Kosa8NotFoundError 404
├─ Kosa8ConflictError 409
└─ Kosa8UnsupportedError 501
Any other status, such as 500, is a plain Kosa8ApiError. All classes are exported,
so instanceof works, and err.name is the class name.
Fields
| Field | On | Meaning |
|---|---|---|
message | all | For a daemon error: "<error> (<remediation>)", the way the CLI prints it. |
status | daemon errors | The HTTP status. |
daemonMessage | daemon errors, stream errors | The daemon's error text, verbatim. |
remediation | daemon errors | The daemon's advice: what to do next. |
method, path | daemon and connection errors | The request that failed, such as POST /kosa8/v1/pull. |
body | daemon errors | The decoded response body. |
cause | connection errors, failed rewinds | The underlying error, such as Node's ENOENT. |
stdout, stderr | Kosa8StreamError | Output produced before the error. |
approval | Kosa8ApprovalRequiredError | The daemon's refusal: why, signers, request_command, action, class, requirement, … |
import { Kosa8Client, Kosa8Error, Kosa8PolicyError, Kosa8CheckpointError } from '@kosa8/sdk';
const kosa8 = new Kosa8Client();
const show = (err) => console.log(`${err.constructor.name} status=${err.status}\n daemonMessage: ${err.daemonMessage}\n remediation: ${err.remediation}\n ${err.method} ${err.path}`);
// 403 from the org policy (here: allowed_registries is ["ghcr.io"]).
await kosa8.pull('docker.io/library/alpine:latest').catch((err) => {
console.log(err instanceof Kosa8PolicyError, err instanceof Kosa8Error);
show(err);
});
// Client-side checkpoint bookkeeping.
await kosa8.rewind('never-checkpointed').catch((err) => console.log(err.constructor.name + ':', err.message));
await kosa8.checkpoint('../etc').catch((err) => console.log(err.constructor.name + ':', err.message));
$ node errors.mjs
true true
Kosa8PolicyError status=403
daemonMessage: policy "sdk-docs-registries" forbids registry "docker.io" (allowed: ghcr.io)
remediation: the org policy restricts which registries may be pulled; see `kosa8 policy show`
POST /kosa8/v1/pull
Kosa8CheckpointError: no checkpoints for "never-checkpointed" — take one with checkpoint() or `kosa8 checkpoint never-checkpointed`
Kosa8CheckpointError: invalid sandbox name "../etc"
(For that run a test policy with "allowed_registries": ["ghcr.io"] was installed,
then removed.)
Each class
Kosa8ConnectionError
When: the request never reached a daemon: no socket file, nothing listening, a socket path too long for the operating system, a remote host that does not answer.
kosa8 daemon unreachable at unix:///tmp/k8d/no-daemon-here.sock: connect ENOENT /tmp/k8d/no-daemon-here.sock (is kosa8 installed and its daemon running? start it with `kosa8 daemon start`)
Handle: start the daemon (kosa8 daemon start) or fix the address.
err.cause.code is Node's error code: ENOENT (no socket),
ECONNREFUSED (socket, no daemon), EINVAL (path too long on macOS). Safe to
retry once the daemon is up; nothing was done.
Kosa8BadRequestError (400)
When: the daemon refused a value, most often an egress rule.
egress allow "*.com": com is a public suffix, under which anyone can register a name, so the wildcard would admit strangers' hosts; allow a domain below it, as in *.example.com
Handle: fix the input; retrying the same request fails the same way. The message says what is wrong with it.
Kosa8AuthError (401)
When: an agent token was presented and is not valid, or a remote worker's bearer token is missing or wrong. A bad agent token is never treated as anonymous.
agent token is not recognised; mint one with `kosa8 agent token create NAME` missing or invalid bearer token (set KOSA8_TOKEN to the worker's token)
Handle: mint a new agent token, or set KOSA8_TOKEN / token. See
Connecting.
Kosa8PolicyError (403)
When: the org policy forbids the action outright: a registry not on the allow-list, a
label rule, require_sandbox. Example above.
Handle: do something the policy allows, or ask whoever manages the policy.
kosa8 policy show prints the active policy.
Kosa8ApprovalRequiredError (403)
When: the policy wants named people to approve this exact action, and the call brought no
usable approval. A subclass of Kosa8PolicyError, so catch it first.
why: no approval was presented with the call signers: [ 'Asha Rao <[email protected]>' ] ask for it with: kosa8 approve request sandbox-rm runner
Handle: show err.approval to a person, wait for the approval id, then repeat
the call with { approval: id }. The whole flow is on
Approvals and policy.
Kosa8NotFoundError (404)
When: no such sandbox or snapshot.
no such sandbox: ro (list sandboxes with `kosa8 sandbox ls`)
Handle: usually ignore it on clean-up paths (the thing is already gone). Note that
getSandbox returns undefined instead of throwing. A container that does not
exist is reported differently, as a stream error (below).
Kosa8ConflictError (409)
When: the request conflicts with current state: a snapshot id already taken, a sealed
snapshot deleted without force.
snapshot already exists: runner-before-upgrade (choose another id, or delete the existing snapshot first)
Handle: choose another id, or resolve the state first. A sandbox name that is taken comes back as 500 in v0.9.10, not 409 (below). From kosa8 0.9.36 it is a 409 too, for create, restore and fork:
sandbox ro already exists (choose another name, or remove the existing sandbox with `kosa8 sandbox rm NAME` (`kosa8 sandbox ls` lists them))
Kosa8UnsupportedError (501)
When: the machine cannot do this: no microVM backend (sandboxes need macOS on Apple silicon, or Linux with KVM), or a guest image from an older release that lacks a feature, in which case the message names the fix. Not reproduced for this page, which ran on a supported machine with a matching guest.
Handle: run on a supported machine; kosa8 doctor says what is missing.
Kosa8ApiError (other statuses)
When: any other HTTP error, usually 500.
sandbox ro already exists (run `kosa8 doctor`; check ~/.kosa8/log/) restore machine state: Error Domain=VZErrorDomain Code=12 Description="An error occurred while restoring the virtual machine. The virtual machine failed to restore with error “permission denied”." … (list snapshots with `kosa8 snapshot ls`)
Handle: read the message. The second one means the Mac's screen is locked; see
Troubleshooting. Both are v0.9.10 messages: from
kosa8 0.9.36 the first is a Kosa8ConflictError (409), and the second is a 503 that
begins "macOS blocks restoring a VM while the screen is locked; unlock and retry". The daemon's log,
~/.kosa8/log/kosa8d.log, has more.
Kosa8StreamError
When: a run, exec or attach started streaming and then
ended with an error instead of an exit code, or the stream ended with no final frame.
image "docker.io/library/python:3.13-alpine" not found in engine: image "docker.io/library/python:3.13-alpine": not found no such container: k99-nope failed to create shim task: OCI runtime create failed: executable file `python3` not found in $PATH: No such file or directory
Handle: pull the image first, check the container id, or check the command exists in the
image. err.stdout and err.stderr hold any output produced before the
failure. A command that ran and exited non-zero is not an error: exitCode is
in the result.
Kosa8CheckpointError
When: checkpoint bookkeeping, done in the client: nothing to rewind to, a checkpoint
to that matches no id or label, a sandbox name that could escape the store, a store
lock not acquired within 10 seconds. Examples above.
Handle: take a checkpoint first, or list them with listCheckpoints.
Kosa8Error (itself)
When: a check in the client, before anything is sent, or a composite operation that failed partway.
egressAllow already limits the network to the hosts it lists; do not also set egress an egress allow-list needs at least one host; use egress: "deny" for no network refusing to talk to 10.0.0.5:7440 over plain http: the token and every request would cross the network unencrypted. Use https://10.0.0.5:7440 (a worker behind a TLS-terminating tunnel or proxy), or set insecure: true / KOSA8_INSECURE=1 if this link is already private and trusted
Other messages of this class in the source: give at least two variants; with one there is
nothing to compare (explore), keep … is not one of the branches (explore), and
restore failed and NAME is now stopped: … — recover with restoreSnapshot(…) (rewind),
whose cause is the daemon's error. In that last case the sandbox has already been
removed and the checkpoint's snapshot is intact.
A handler that covers the cases
import {
Kosa8ApprovalRequiredError, Kosa8PolicyError, Kosa8ConnectionError,
Kosa8NotFoundError, Kosa8StreamError, Kosa8Error,
} from '@kosa8/sdk';
try {
// ... a kosa8 call ...
} catch (err) {
if (err instanceof Kosa8ApprovalRequiredError) {
// ask a person; retry later with { approval: id }
notifyApprovers(err.approval.why, err.approval.signers, err.approval.request_command);
} else if (err instanceof Kosa8PolicyError) {
// refused by policy, no approval possible
} else if (err instanceof Kosa8ConnectionError) {
// daemon down: start it, or fail fast
} else if (err instanceof Kosa8NotFoundError) {
// already gone
} else if (err instanceof Kosa8StreamError) {
// the command could not run; err.stdout / err.stderr hold partial output
} else if (err instanceof Kosa8Error) {
console.error(err.message); // includes the daemon's remediation
} else {
throw err; // not from kosa8 (an AbortSignal reason, for example)
}
}
That block is a pattern, not a program: notifyApprovers is yours to write. Note that
an aborted call rejects with the signal's reason (a TimeoutError or
AbortError), not a Kosa8Error.