Guides / JavaScript & TypeScript / Troubleshooting
Troubleshooting
The problems below were all met while writing and testing these pages, and the messages are quoted as kosa8 v0.9.10 and the 0.1.0 packages printed them. Search the page for a few words of yours.
The daemon is not running
Kosa8ConnectionError: kosa8 daemon unreachable at unix:///tmp/k8d/.kosa8/run/kosa8.sock: connect ENOENT /tmp/k8d/.kosa8/run/kosa8.sock (is kosa8 installed and its daemon running? start it with `kosa8 daemon start`)
What to do. kosa8 daemon start, then kosa8 daemon status. If it
will not start, read ~/.kosa8/log/kosa8d.log and run kosa8 doctor. After
a daemon restart, every sandbox is stopped: startSandbox boots it from its disk.
Also this message: if a v0.9.10 daemon was started with --listen, it serves
TCP instead of the socket, and the SDK needs KOSA8_HOST and KOSA8_TOKEN; see
Connecting. Fixed in kosa8 0.9.36: --listen
keeps the unix socket, so the default client works there too.
The socket path is too long (macOS)
kosa8: kosa8d did not become ready; check /private/tmp/claude-501/…/longhome/.kosa8/log/kosa8d.log kosa8d: server stopped: listen unix /private/tmp/claude-501/…/longhome/.kosa8/run/kosa8.sock: bind: invalid argument Kosa8ConnectionError: kosa8 daemon unreachable at unix:///private/tmp/claude-501/…/longhome/.kosa8/run/kosa8.sock: connect EINVAL …
Why. macOS limits a unix socket path to 104 bytes, and the socket lives at
$HOME/.kosa8/run/kosa8.sock. That one was 131 bytes. It happens in CI, containers and
test harnesses that set HOME to a deep temporary folder.
What to do. Use a shorter HOME, or a short symlink to it
(ln -s /very/long/dir /tmp/k8 && HOME=/tmp/k8), for both the daemon and your
program. The tests for these pages ran with HOME=/tmp/k8d.
"permission denied" when restoring, on a Mac
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”." UserInfo={
NSLocalizedFailure = "An error occurred while restoring the virtual machine.";
NSLocalizedFailureReason = "The virtual machine failed to restore with error \U201cpermission denied\U201d.";
} (list snapshots with `kosa8 snapshot ls`)
Why. Apple's Virtualization framework protects a saved machine's state with a key held
in the Secure Enclave, and that key can only be used while the Mac is unlocked. With the screen
locked, every restore fails: rewind, fork, explore,
restoreSnapshot, and the AI SDK adapter's resume and templates. Saving still works, so
checkpoints and snapshots are taken without complaint and fail only when you go back to them. The
system log shows the cause:
$ log show --last 5m --predicate 'process CONTAINS[c] "virtualization" AND messageType == error'
… [com.apple.security:seckey] SecKeyCreateDecryptedDataWithParameters failed: Error Domain=NSOSStatusErrorDomain Code=-25308 "<sepk:p256(u) kid=97d7a08b485b3ab3>: unable to compute shared secret" UserInfo={NSDebugDescription=<sepk:p256(u) kid=97d7a08b485b3ab3>: unable to compute shared secret, AKSError=-536870174}
Code -25308 is errSecInteractionNotAllowed. In our tests every restore
failed for as long as the screen was locked, and the same calls worked as soon as it was
unlocked.
The key is the framework's own, not kosa8's: kosa8 keeps no keychain or Secure Enclave key on the restore path, so there is no setting on its side that lifts the restriction. Saving needs only the public half of the key, which is why it keeps working.
From kosa8 0.9.36 the error says so plainly, keeps Apple's text after it, and comes back as
HTTP 503 (a plain Kosa8ApiError with status 503) rather than 500:
macOS blocks restoring a VM while the screen is locked; unlock and retry (restore machine state: Error Domain=VZErrorDomain Code=12 …) (unlock the Mac, then retry; snapshots can still be taken while it is locked, but nothing can be restored, forked or rewound)
What to do. Keep the Mac unlocked while jobs that restore are running, or run them on
Linux. If a rewind failed this way, the sandbox has been removed and its checkpoint is intact:
unlock, then run the restoreSnapshot(…) call the error message gives. On v0.9.10 a
failed fork leaves debris; see below. From 0.9.36 a failed fork leaves nothing.
Disk space
Sandboxes are cheap on disk until they are not. Things that use real space:
- The guest image, about 1.5 GB in
~/.kosa8/guest, once. - Memory saves. Every checkpoint, snapshot and fork writes the guest's memory state: about 80 MiB for an idle 1 GiB sandbox in our tests, more for a busy one.
- Writes to cloned disks. Disks are copy-on-write clones, so a copy costs nothing until either side writes, then it costs what was written.
- Memory of running copies. On a Mac, many running sandboxes push memory into swap, and swap lives on the same disk.
- Wipes and encryption.
wipe: trueandencrypt: trueoverwrite data, and on APFS an overwrite goes to new blocks, so they need free space while they run.
What running low looked like:
snapshot runner-encrypted was taken but not encrypted: encrypted, but the plaintext of disk.img could not be wiped; run `kosa8 snapshot encrypt runner-encrypted` again fork 3/3: sandbox tz3 already exists (sandbox must be running)
The second came on the retry after a four-way explore failed partway on a nearly full disk. The failed fork left two copies and its snapshot behind, and its own files, which no listing showed. Fixed in kosa8 0.9.36: a fork keeps all of its copies or none. If one copy fails, the copies already made are removed with their disks, the fork's snapshot is deleted and every name is free again; the error says so:
fork 3/4: no space left on device (nothing was kept: the 3 copies already made were removed, with the fork's snapshot, and the names are free again)
What to do. Check free space before big operations (df -h ~). Keep sandboxes
small (memMb) when you fork them. Delete what you are done with:
listSandboxes() and listSnapshots() show leftovers, including fork snapshots
named <sandbox>-fork-… and adapter templates named aisdk-tpl-….
On v0.9.10, after a failed fork, retry with a new prefix. Files left in
~/.kosa8/sandboxes/ with no matching sandbox in kosa8 sandbox ls are
debris from a failed operation.
Node version
@kosa8/sdk needs Node 18 or later; @kosa8/ai-sdk-sandbox needs 22 or
later, because @ai-sdk/harness does. Both declare it in engines, so npm
warns at install time on an older Node. Check with node --version. These pages were
tested on Node 25.9.0 only; older versions were not run.
A request from inside the sandbox fails
wget: bad address 'github.com' urllib.error.URLError: <urlopen error [Errno -3] Try again> ConnectionRefusedError: [Errno 111] Connection refused
Why. The sandbox's egress rule does not allow that host. A name not on the list does not resolve, and a connection to an address not on it is refused. Nothing is thrown in Node: the command fails with its own error and a non-zero exit code.
What to do. sandboxEgress(id) shows the rule and its counters;
kosa8 audit ls shows each sandbox.egress.deny. The rule cannot be changed
on a running sandbox: create a new one with the host in egressAllow. For a model API,
use a broker route rather than allowing the host and
putting the key inside.
Through the broker's proxy, busybox wget got HTTP/1.1 400 Bad Request
for HTTPS sites on v0.9.10; use curl, which works. Fixed in kosa8 0.9.36: busybox has no
CONNECT and sends GET https://host/path to the proxy, which the broker now
forwards, making the TLS connection and checking the certificate itself.
A call needs approval
Kosa8ApprovalRequiredError: policy "sdk-docs-test" requires 1 signed approval(s) for destructive, from people other than the requester: `sandbox-rm runner` needs signed approval before it can run — no approval was presented with the call (…)
What to do. Follow Approvals and policy: a person
requests, an approver signs, you repeat the call with { approval: id }. Three things
that went wrong for us:
--reasonwritten after the action is recorded as part of the action on v0.9.10. Put flags first:kosa8 approve request --reason "…" sandbox-rm runner. Fixed in kosa8 0.9.36: the refusal's hint prints flags first, and a--reasonat the very end is read as the reason.- "needs 2 distinct approver(s)" with a policy that says 1: the requester is not listed in the policy. List them.
- "was already used": an approval covers one call. Methods like
rewindmake several calls; see composite calls.
An AI SDK script does not exit
The program prints everything and keeps running. With
process._getActiveHandles() you would see one Socket per affected
command. This is a known issue in @kosa8/ai-sdk-sandbox 0.1.0, fixed in 0.1.1; with
0.1.0, end the script with process.exit(). Details on
the adapter page.
Other things that surprised us
| Symptom | Cause and fix |
|---|---|
The first createSandbox takes 30 seconds or more | The daemon is downloading and checking the guest image (35 s in our test). Later creates take about a second. |
Kosa8StreamError: image "…" not found in engine | run does not pull. Call pullIntoSandbox first. |
A file written in one run is missing in the next | Each run is a new container. Use startContainer and exec, or a mount. The same holds for explore variants. |
A timeout inside the container never fires | The command became PID 1, which ignores signals. Keep a shell as PID 1: sh -c 'timeout -s KILL 10 cmd; exit $?'. See the recipe. |
A command keeps running after an aborted run | Aborting ends the request, not the process. Limit time inside the sandbox. |
A second writeStdin has no effect | Stdin closes when the first body ends. Pass an async iterable to stream input over time. |
| Creating a sandbox with a taken name gives HTTP 500 | v0.9.10 reports it as Kosa8ApiError, "sandbox NAME already exists", not as a 409. Fixed in kosa8 0.9.36: a Kosa8ConflictError (409), for create, restore and fork. |
| "Exec format error" | An image built only for the other CPU architecture. Nothing is emulated: use an arm64 image on Apple silicon, amd64 on x86-64 Linux. |
A background process started inside the adapter's run is gone | It does not outlive the command that started it. Run long-lived processes as a container's main process. |
For refusals that come from an agent's tool calls and MCP, see Agent troubleshooting.