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.
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.
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.
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.
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".
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.