kosa8 docs

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

FieldOnMeaning
messageallFor a daemon error: "<error> (<remediation>)", the way the CLI prints it.
statusdaemon errorsThe HTTP status.
daemonMessagedaemon errors, stream errorsThe daemon's error text, verbatim.
remediationdaemon errorsThe daemon's advice: what to do next.
method, pathdaemon and connection errorsThe request that failed, such as POST /kosa8/v1/pull.
bodydaemon errorsThe decoded response body.
causeconnection errors, failed rewindsThe underlying error, such as Node's ENOENT.
stdout, stderrKosa8StreamErrorOutput produced before the error.
approvalKosa8ApprovalRequiredErrorThe 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.