Skip to content

Errors

The SDK reports failures as VMError, exported from @oxidevm/sdk. Promise-based methods reject with it; synchronous ones, such as vm.workspace.watch, throw it.

VMError
class VMError extends Error {
readonly code: VMErrorCode;
readonly detail: Readonly<Record<string, unknown>> | undefined;
constructor(code: VMErrorCode, message: string, detail?: Readonly<Record<string, unknown>>);
}
Member Meaning
code One of the fifteen stable VMErrorCode values.
detail Optional structured context. detail.reason, when present, is a stable string that narrows the cause. See Reasons.
message A human-readable explanation for logs. Its wording is not stable.
name Always "VMError".

Test with error instanceof VMError.

open-project.ts
import { createVM, VMError, type UserspaceOptions, type UserspaceVM } from "@oxidevm/sdk";
export async function openProject(options: UserspaceOptions): Promise<UserspaceVM> {
try {
return await createVM({ ...options, restorePoint: "latest" });
} catch (error: unknown) {
if (
error instanceof VMError &&
error.code === "PROTOCOL_ERROR" &&
error.detail?.["reason"] === "image-trust-changed"
) {
// Saved on an image signed by another key: move it to the configured image, keeping /workspace.
return createVM({ ...options, restorePoint: "update-image" });
}
throw error;
}
}
Code Typical cause What to do
ABORTED An AbortSignal you passed fired, or the VM was disposed while the operation ran, for example during a workspace watch. Treat it as a cancellation.
CORE_LOAD_FAILED A self-hosted engine’s files at runtime.baseUrl could not be downloaded or differ from the release the SDK pins (detail.asset names the file, detail.status a failed HTTP status), or the browser lacks a required WebAssembly or JavaScript feature (detail.reason: "browser-unsupported"). Self-hosted, stage the engine again with oxidevm assets install, serve .wasm as application/wasm, and check the page with oxidevm doctor.
FEATURE_NOT_AVAILABLE Something this call needs is missing in the browser, the image, the mode or your configuration. Read detail.reason, then fix the configuration or hide the feature.
GUEST_CRASH The guest failed in a way the SDK cannot recover from, for example a restored guest agent that does not answer (agent-unavailable). Dispose the VM and create a new one; offer a cold start with restorePoint: "none".
INVALID_ARGUMENT An option, path or argument failed validation, or a backup file was refused. Fix the call. This is a caller error.
PERMISSION_DENIED The browser refused storage access, a workspace entry is not accessible, a relay refused the user or the destination, or a public runtime has no license token that admits this page (license-required, license-invalid). Ask for the needed permission, check the relay’s policy, or see Licenses.
PROTOCOL_ERROR A runtime or wire fault: a call on a disposed VM, an image manifest that could not be downloaded or verified, an image signed by another key, or assets that do not match the installed SDK. Check that the staged assets match the installed SDK; for key changes see image-trust-changed and untrusted-key below.
QUOTA_EXCEEDED Browser storage is full, exec output passed maxOutputBytes (detail.maxOutputBytes), or a relay quota was reached. Free space, for example by deleting restore points; raise maxOutputBytes or stream with spawn.
RELAY_ERROR A relay connection failed. detail.reason names the relay failure. Show the reason and let the user retry vm.network.connect.
SNAPSHOT_VERSION A restore point or backup uses a format this SDK cannot read. Use the SDK that wrote it, or start without it.
STORAGE_ERROR A workspace or browser storage operation failed. Branch on detail.reason.
STORAGE_NOT_FOUND A workspace path, a restore point or a stored image does not exist. Check the path or reference; for images see image-not-cached and base-missing.
UNSUPPORTED_INSTRUCTION The guest executed an x86-64 instruction or CPU feature that the engine does not implement. Report it with the program that triggered it.
UNSUPPORTED_SYSCALL Execution reached a system call path that the engine does not handle. Report it with the command that triggered it.
VM_FAULT The engine hit an internal execution fault, or a command failed without a more specific error. Dispose the VM, create a new one, and report it.

VMErrorCode is the union of these fifteen strings.

Field Appears with Meaning
reason many codes A stable cause. See Reasons.
cleanupFailed the original code true when cleaning up after the failure also failed, for example after a failed createVM or an aborted exec.
cleanupError with cleanupFailed The cleanup failure as { code, message, detail? }.
maxOutputBytes QUOTA_EXCEEDED The output limit exec enforced.
size STORAGE_ERROR with too-large The size of the file or write, in bytes.
keyId PROTOCOL_ERROR with untrusted-key or image-trust-changed The key ID involved.
missing CORE_LOAD_FAILED with browser-unsupported The missing browser features: "wasm-simd", "wasm-tail-calls", "promise-with-resolvers".
requirement FEATURE_NOT_AVAILABLE with browser-unsupported "cross-origin-isolation" when a page that uses the runtime frame is not cross-origin isolated.
rejected PERMISSION_DENIED with license-required or license-invalid Why the engine refused the license token: a LicenseRejection, or null when it reports none.
errno Userspace workspace errors without a named reason The Linux error number.
detail.reason Code Meaning
browser-unsupported FEATURE_NOT_AVAILABLE The browser has no module workers, or a page that uses the runtime frame is not cross-origin isolated (detail.requirement: "cross-origin-isolation").
browser-unsupported CORE_LOAD_FAILED The browser lacks a required feature; detail.missing lists it. See Browser requirements.
release-gate-open FEATURE_NOT_AVAILABLE System vcpus other than 1.
not-configured FEATURE_NOT_AVAILABLE exposePort on a VM created without preview.
image-unsupported FEATURE_NOT_AVAILABLE The image’s guest integration lacks the operation, for example terminal resize or interrupt, or the requested signal.

Browser storage refusals use PERMISSION_DENIED with detail.reason: "browser-storage-blocked". This is separate from license validation. Allow site storage for the application and runtime origin, then retry. The SDK cannot change browser privacy settings. It does not replace durable storage with temporary memory.

The engine checks runtime.license at new VM creation, including creation from a restore point. Public sites need a token. See Licenses. Pages on localhost, a name ending in .localhost, a 127.0.0.0/8 address or [::1] need no token.

detail.reason Code Meaning
license-required PERMISSION_DENIED A public site got no token. detail.rejected is "public-requires-license" in the official frame, or "self-host-requires-license" for self-hosting.
license-invalid PERMISSION_DENIED The engine did not accept the token for this page; detail.rejected says why.

detail.rejected is a LicenseRejection:

Value Meaning
malformed The token is not a well-formed license token.
unsupported-version The token uses a format this engine does not read.
unknown-key The token is signed with a key this engine does not trust.
bad-signature The signature does not verify.
expired The license has passed its expiry grace period.
not-yet-valid The license is not valid yet.
origin-mismatch The license is valid, but for other origins.
not-self-host-license The license covers this origin, but not self-hosting.
self-host-requires-license A self-hosted engine got no token.
public-requires-license A public embedding page needs a registered token.
issuer-policy-violation The token requests permissions or a lifetime its signer cannot grant.

An invalid token never falls back to free public admission. Older published SDK/runtime versions retain their previous behavior. Running VMs continue when a token expires. License failures do not delete files, caches or restore points.

detail.reason Code Meaning
workspace-in-use STORAGE_ERROR Another VM, in this tab or another, holds this workspaceId.
too-large STORAGE_ERROR A read above maxBytes, or a write above 16 MiB.
version-mismatch STORAGE_ERROR write with ifVersion found another version, or no file.
exists STORAGE_ERROR write with ifVersion: null found an entry, or rename found an entry at the target.
not-a-file STORAGE_ERROR read or write met a directory or another non-file.
not-a-directory STORAGE_ERROR A path component is not a directory, or a watched path is not a directory.
busy STORAGE_ERROR The entry stayed busy in the guest after the SDK’s own retries.
image-update-required FEATURE_NOT_AVAILABLE System: the image’s guest agent has no workspace file operations.
no-root-filesystem FEATURE_NOT_AVAILABLE Userspace: the VM has no root filesystem.
detail.reason Code Meaning
image-not-cached STORAGE_ERROR, sometimes STORAGE_NOT_FOUND Image data needed locally is missing or damaged. With imagePreparation: "before-ready" the SDK does not download it implicitly; repair it with vm.prepareImage().
base-missing STORAGE_NOT_FOUND The image that a restore point or a workspace needs is no longer stored in this browser.
untrusted-key PROTOCOL_ERROR The image is signed by another key than the reference’s trust. detail.keyId names the image’s key. Regenerate the reference with oxidevm assets install --image <bundle> --reference <file>.
image-trust-changed PROTOCOL_ERROR A Userspace workspace was saved on an image signed by another key; detail.keyId names that earlier key. The saved state is never booted. Open it with restorePoint: "update-image" to keep its /workspace files on the configured image, or use another workspaceId.
detail.reason Code Meaning
runtime-mismatch FEATURE_NOT_AVAILABLE The restore point belongs to the other mode.
js-process-active FEATURE_NOT_AVAILABLE snapshots.capture or suspend while a JavaScript process (spawnJS or acceleration) runs. Stop it first.
snapshot-older-format SNAPSHOT_VERSION The restore point was written by an older SDK in a format this one cannot read.
snapshot-newer-format SNAPSHOT_VERSION The restore point needs a newer SDK.
agent-unavailable GUEST_CRASH The restored guest agent did not answer.
backup-wrong-file INVALID_ARGUMENT The file is not an OxideVM backup.
backup-damaged INVALID_ARGUMENT The backup is damaged or incomplete.
backup-other-environment INVALID_ARGUMENT The backup belongs to another environment.
backup-newer SNAPSHOT_VERSION The backup needs a newer SDK.
backup-older SNAPSHOT_VERSION The backup comes from an older SDK and cannot be opened.
backup-no-space QUOTA_EXCEEDED There is not enough space in this browser.

vm.network.connect, and createVM with network: { mode: "relay" }, report the RelayFailureReason in detail.reason. vm.network.state carries the same value when its kind is "failed".

detail.reason Code
permission-denied, destination-denied PERMISSION_DENIED
quota-exceeded QUOTA_EXCEEDED
protocol-error PROTOCOL_ERROR
relay-not-found, discovery-failed, approval-denied, approval-expired, authentication-failed, version-mismatch, timed-out, disconnected, connection-refused, connection-reset, network-unreachable, host-unreachable RELAY_ERROR

Invalid connection options fail with INVALID_ARGUMENT before any connection starts. The relay’s version-mismatch comes with RELAY_ERROR, while the workspace reason of the same name comes with STORAGE_ERROR, so check code as well as detail.reason.

detail.reason Code Meaning
local-data-in-use STORAGE_ERROR eraseLocalData() from @oxidevm/sdk/diagnostics found a workspace still open in another tab. Nothing was erased.

Errors of the oxidevm command line use the same codes; see CLI.