This page lists what PiShip needs on a machine and what each PiShip error code means. A failing command prints CODE: message, and often an action; doctor (<command> doctor, or node packages/cli/dist/bin.js doctor <artifact|id>) reports the same areas without starting a session. In the tables, <command> is the distribution's branded command, such as acmecode or mypi.
| Need | When | Details |
|---|---|---|
| Node.js 22.19.0 or newer, and npm | Always, to build from source | The engines field in package.json. An installed release carries its own payload but still runs on the user's Node.js |
| npm 11 or later | piship lock of a manifest that declares Pi packages |
Node 22 bundles npm 10, which piship lock refuses for packages (manifest); run npm install -g npm@11. piship build works with any npm |
Linux: bubblewrap (bwrap) with unprivileged user namespaces |
sandbox.required: true with the default native provider, as in the managed demo |
On Ubuntu 24.04, and other systems where kernel.apparmor_restrict_unprivileged_userns is 1, AppArmor blocks unprivileged user namespaces. Allow them with sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 (as CI does), or with an AppArmor profile for bwrap. Otherwise the launch fails with SANDBOX_UNAVAILABLE |
| macOS: Seatbelt | The same | Built in (/usr/bin/sandbox-exec); nothing to install |
| Windows | The same | There is no native sandbox on Windows: a required native sandbox fails with SANDBOX_UNAVAILABLE. Use a remote backend (sandbox) or sandbox.required: false |
| A secret store | storage.provider: system (the managed demo, login) |
macOS Keychain (/usr/bin/security), Linux Secret Service (secret-tool from libsecret, with an unlocked keyring such as GNOME Keyring), or Windows Credential Manager. Without one, login fails with SECRET_STORE_UNAVAILABLE; PiShip never falls back to a file on its own. storage: {provider: file} keeps owner-only plaintext files instead (a managed manifest must also set acknowledgePlaintext: true) (credentials) |
Releases are built for linux-x64, darwin-arm64, and win32-x64. Pi's interactive mode can download fd and rg on its first start (security); a managed launch never does, and warns fd not found. Offline mode enabled, skipping download. unless the distribution bundles them (bundled search tools) or they are on PATH.
Every code PiShip reports is defined in packages/contracts/src/errors.ts. A retryable error says so and may give Retry after: <n> s.
| Code | Meaning | What to do |
|---|---|---|
CONFIG_INVALID |
The manifest, a preference, a state file, or the install layout is invalid: a field value, a path, overlapping state, install, and bin homes, a refused setting, or --yolo where it means nothing (with a subcommand, --help, or --version, or in a distribution that declares no policy) |
Fix what the message names. node packages/cli/dist/bin.js validate <manifest> checks a manifest. For overlapping homes, point PISHIP_STATE_HOME, PISHIP_INSTALL_HOME, and PISHIP_BIN_HOME at separate directories |
CONFIG_UNAVAILABLE |
Something the launch needs cannot be read: an unset ${NAME} runtime variable (for a network.tls.additionalCA variable, also in the shell that runs update or rollback), an unreadable CA bundle, or a required MCP server's configuration. For login: the registered redirect port (such as 8765) is in use |
Set the runtime variables validate lists. For a busy login port, finish or cancel the other login, or stop the process the error names |
IDENTITY_REQUIRED |
No one is signed in, sign-in was cancelled or timed out, or the signed-in user changed in another session | At a terminal in a managed distribution, <command> signs in on the spot and continues. Otherwise run <command> login and complete sign-in in the browser, then start <command> again (at a terminal, a managed login goes on into the session by itself). Over SSH, forward the redirect port first (remote shells). A timeout that names a provider error, or a browser that showed one, means the client registration needs checking |
IDENTITY_INVALID |
The identity session or provider is not acceptable: another principal, a misconfigured issuer, or a workload identity adapter that failed, returned no expiry, or tried to open a browser | Run <command> login again, or contact your administrator. Administrators: check the configured issuer and client registration; for a workload identity, check its token source and the adapter (identity) |
IDENTITY_EXPIRED |
The identity session expired and cannot be refreshed | Run <command> login. If it keeps happening, check the system clock; for a workload identity, check that its token source is current |
CREDENTIAL_REQUIRED |
No runtime credential or local secret is stored | Run <command> login |
CREDENTIAL_ACQUIRE_FAILED |
The credential provider could not issue a usable credential, or another process is still signing in or refreshing | Try again when the other session finishes. Make the state directory writable. Administrators: check the broker and that it matches inference.baseUrl |
CREDENTIAL_DENIED |
The credential broker denied this user or distribution | Ask your administrator for access |
CREDENTIAL_EXPIRED |
The runtime credential expired and could not be renewed, or the broker issued one already expired | Run <command> login. For a credential the broker issued already expired, detail holds expiresAt, localTime, and, when the broker sent a Date header, brokerTime and clockSkewSeconds: check this computer's clock first (signing in again cannot help while it is wrong), then ask an administrator to check the broker's clock and credential lifetime |
CREDENTIAL_REVOKED |
The gateway rejected the runtime credential or the stored key | PiShip re-acquires once. If it persists, run <command> login again; for a local secret, check the key first |
SECRET_STORE_UNAVAILABLE |
The secret store is missing, locked, or failed to read or delete a secret. The message leads with the store's own problem, such as secret-tool was not found |
Unlock or repair the secret store, then run the same command again. On Linux without a desktop session (WSL, SSH, a headless server) see no Secret Service on Linux; see prerequisites for what each platform needs |
GATEWAY_UNREACHABLE |
A request to the identity provider, credential broker, gateway, or a workload identity adapter failed or timed out, or the gateway's model provider refused it. When the message names a proxy, the proxy failed (refused the connection, never opened the tunnel, or could not reach the host), not the service. Retryable | Check the network connection, then try again. For a proxy, check that it is running and that HTTPS_PROXY and HTTP_PROXY name it, or list the host in NO_PROXY. Administrators: check the gateway's provider configuration |
GATEWAY_RATE_LIMITED |
The gateway or another service answered 429. Retryable | Wait for the time shown, then try again |
GATEWAY_PROTOCOL_ERROR |
The gateway answered with an unexpected status or a model list that is not valid | Administrators: check that inference.baseUrl points at an OpenAI-compatible gateway and that it is healthy |
REQUEST_CANCELLED |
The caller cancelled the request before it finished, for example a --smoke-model acceptance request that was aborted. Nothing failed on the gateway |
Run the command again when you want the request to complete |
MODEL_DENIED |
The model is not allowed by the distribution or a policy rule, or no default model is configured. For a piship/v1alpha6 virtual model: a request routed to a target outside its declared routes, or one model.dispatch denies |
Choose one of the allowed models the action lists (<command> models) |
MODEL_UNAVAILABLE |
No allowed model is currently available, or the gateway does not serve the selected one | Choose one of the models the action lists, or try again later |
MODEL_INCOMPATIBLE |
The selected model does not meet the requirements of an enabled capability, or its metadata is unknown; no other model is substituted | Select a model that meets the requirements (<command> capabilities shows them), or ask the distribution owner to adjust them (inference) |
NETWORK_DENIED |
The network policy refused a host: plain HTTP to a non-loopback host (for an update source or another endpoint, a public host, or a private one the distribution forces to HTTPS-only with updates.transport: https or httpTransport: https), or an undeclared host under network.privateOnly. Or the proxy the message names refused the request: HTTP 407 (it requires authentication) or 403 (it does not allow the host) |
Use HTTPS. Distribution owners: declare the host through a managed endpoint or network.allowHosts; an update channel or endpoint on an intranet without HTTPS works over plain HTTP unless the manifest sets https for it (manifest). For a 407, put the proxy credentials in the proxy URL or ask the proxy administrator how this machine authenticates; for a 403, ask them to allow the host |
TLS_POLICY_VIOLATION |
NODE_TLS_REJECT_UNAUTHORIZED=0 would disable TLS verification, or the certificate of the host (or HTTPS proxy) the message names failed verification: an untrusted chain (such as UNABLE_TO_VERIFY_LEAF_SIGNATURE or SELF_SIGNED_CERT_IN_CHAIN), an expired certificate, or one that does not name the host |
Unset NODE_TLS_REJECT_UNAUTHORIZED. For an untrusted chain, declare the enterprise CA bundle in network.tls.additionalCA (quote a reference: ["${CORP_CA_BUNDLE}"]); never turn verification off. doctor's Network group checks the proxy, the bundles, and the path to each endpoint |
POLICY_DENIED |
The distribution's policy refused a command or setting: an enforced key, a preference that widens an allowlist, a channel outside updates.channels, or login/logout in a distribution that delegates authentication to Pi, or auto on (/auto on) or the --yolo launch option where the distribution does not allow auto mode (policy.userAuto is off). In piship/v1alpha6 also: a virtual model registered after launch or from an undeclared router, a tool registered after launch in a managed distribution, or Codemode while a tool it can reach is decided only by the policy default |
Use a value the distribution allows. In a Pi-native distribution, use Pi's /login and /logout inside the session. For auto mode or --yolo, ask the administrator to allow it or to allow the actions you need, or start without --yolo (prompts) |
POLICY_UNENFORCEABLE |
A managed distribution has a deny or ask rule on an action PiShip cannot enforce (no runtime hook), such as web.request or session.export public through the gist fallback |
Remove the rule, or list it in policy.acknowledgeUnenforced as <action>:<resource> to record that the gap is known; doctor shows acknowledged gaps |
RADIUS_PROVIDER_RESERVED |
A gateway (openai-compatible) distribution has the app.id radius. Its model provider takes the distribution ID, so Pi's /share would upload the session to Radius with the distribution's credential. validate and lock refuse it |
Choose another app.id (manifest) |
MCP_DENIED |
Policy denies a required MCP server | Ask the distribution owner; a required server that is denied stops the launch (manifest) |
MCP_UNHEALTHY |
An MCP server cannot start or failed its checks, for example a wrong serverInfo.name or a credential: runtime server on another origin than inference.baseUrl |
Check the server's command or URL and the doctor MCP section. A required server stops the launch; an optional one is marked failed |
SANDBOX_UNAVAILABLE |
The distribution requires a sandbox that cannot be used: the OS sandbox is missing or blocked, a backend's configuration or runtime variables are wrong, or the backend cannot enforce the declared network mode. PiShip never falls back to running unsandboxed | See prerequisites. Fix the sandbox backend configuration or its runtime variables (sandbox) |
AUDIT_UNAVAILABLE |
A required audit sink is failing; governed actions fail closed until it recovers | Restore the audit sink or contact the distribution administrator, and report any unrecorded activity |
LOCK_INVALID |
piship.lock is missing or stale, or an installed payload's manifest or npm lock no longer matches its lock |
Run node packages/cli/dist/bin.js lock <manifest> and rebuild. For an installed distribution, restore the release with piship repair <id> <release archive> (repairing an installed release) |
INTEGRITY_FAILED |
A payload, release archive, or update does not match its inventory, checksums, or signed channel entry. For an installed payload, the message names each offending path as unexpected (not in the inventory), modified, or missing. Also, at lock, build, or release, a Pi package whose npm integrity, tree digest, or stored lockfile no longer matches piship.lock |
Do not install or run it. For an installed distribution, restore the release with piship repair <id> <release archive> (repairing an installed release); otherwise obtain the artifact again from the trusted source. Report a bad update source to the distribution owner |
UPDATE_FAILED |
An update cannot proceed: no update source or trusted key, a downgrade, an install without release tracking, a migration that needs review, or local data no release can read | Follow the message: pass --from, add updates.trust.bootstrap, reinstall, review the migration check and rerun update --accept-review, or move the damaged file the message names aside and run update again. A download cut short (sleep, a network change, a stalled connection) is retryable and says so: run update again (update lifecycle) |
ROLLBACK_FAILED |
There is no retained release, it is newer than the active one, it failed verification, or it cannot read the current state | Use update instead where the message says so. For a damaged state file the message names, move it aside and run rollback again. A retained release that failed verification is not activated: restore it with piship repair <id> <release archive of that version> and roll back again |
storage.provider: system on Linux uses the Secret Service through libsecret's secret-tool. WSL, an SSH session, and a headless server often have neither secret-tool nor an unlocked keyring, and login then fails with SECRET_STORE_UNAVAILABLE, for example Linux Secret Service secret store is unavailable: secret-tool was not found (spawnSync secret-tool ENOENT). Either:
- install and unlock a Secret Service keyring and
secret-tool(on Debian and Ubuntu, thegnome-keyringandlibsecret-toolspackages, with a D-Bus session and the keyring unlocked), then run<command> loginagain; or - set
credential.storage: {provider: file}inpiship.yaml(a managed manifest also setsacknowledgePlaintext: true), then lock and build the distribution again and run<command> login. Secrets are then owner-only plaintext files under the state directory, not platform secure storage.
A sign-in that stops because the store cannot be reached leaves nothing behind. A discarded marker lists the references it names that were never confirmed written (unconfirmed: a first sign-in names its new reference before it writes the secret, until the write succeeds). When the marker's store command is not installed, those references are dropped at the next login, logout, sandbox login, or purge, and the command says which; so switching to the file store is enough. A confirmed reference (a secret a logout or replacement could not delete, or one a first sign-in wrote before it was interrupted) stays tracked and fails closed even then, since the command may be missing only now (a reduced PATH in a launcher, an uninstalled package) while the secret exists: restore the command, then run <command> logout. A marker left by v0.8.0 has no unconfirmed list and cannot tell the two apart, so all of its references are treated as unconfirmed and dropped when the command is not installed; one that names a secret that really exists leaves it in the platform store, never used: remove it with the platform store's own tool (Keychain Access, Credential Manager, or a Secret Service browser such as Seahorse) if it matters. References whose store answers but fails (a locked keyring, no D-Bus session) stay tracked, never used: <command> doctor reports them, and <command> login or <command> logout deletes them once that store works.
A policy ask prompts each time the action happens; headless (--smoke, no terminal or UI) it is denied with "approval needs an interactive session" and recorded as a denial (the specification's APPROVAL_REQUIRED). <command> policy explain <action> <resource> names the rule. If the distribution allows auto mode (<command> auto status says so, or doctor shows an auto line in the Policy group), <command> auto on (or /auto on in a session) approves an ask from the distribution defaults without a prompt, headless too; each approval is audited. An ask from an enforced, team, project, or your own rule still prompts, and a denial stays a denial. If auto status says it was reset, another identity signed in since it was turned on: turn it on again. If it says the release does not allow it, an update turned auto mode off for this distribution. Otherwise ask the administrator to allow the action (user auto mode). <command> --yolo approves the asks of one session without storing anything: in a personal distribution every ask, in a managed one what auto mode approves, and only where policy.userAuto is allowed; elsewhere it fails at startup with POLICY_DENIED ("--yolo is not allowed: this distribution does not allow auto-approval"), and with a subcommand, --help, or --version it fails with CONFIG_INVALID ("--yolo applies only when a session starts"). A deny stays a denial either way (--yolo).
doctor (the branded <command> doctor and piship doctor <id>) verifies an installed release against its inventory, so any change to its payload directory makes it fail with INTEGRITY_FAILED. A routine start checks only piship.lock against the digest the install receipt recorded (a changed one stops it with INTEGRITY_FAILED), and update and rollback do not re-hash the payload either, to keep them fast on machines that scan every file, so another changed file is found by doctor, or at launch when the distribution sets runtime.verifyAtLaunch: true; a missing or damaged file can still break the launch itself, so the first launch after each boot also checks that every file of the inventory exists and is not empty, and names the repair command when one is not. Nothing is exempt from the check, including operating-system metadata such as .DS_Store, ._*, Thumbs.db, and desktop.ini: an exemption would let any file of those names ride along unchecked. The message names the payload and the offending paths, up to five per kind and a count of the rest (the error detail carries up to 50):
INTEGRITY_FAILED: Installed payload integrity mismatch in <install-home>/apps/acmecode/1.1.0; unexpected (not in the inventory): .DS_Store
Action: Do not run it. Remove the unexpected files it names, or restore it from a trusted release of the same version with: piship repair acmecode <release archive>, or without a PiShip CLI: node <extracted release>/payload/piship.mjs repair acmecode <extracted release> (repair does not run this payload; never run its piship.mjs). …
update and rollback run through the active release, so a damaged one can fail them and they cannot fix it themselves. piship repair runs in PiShip instead and never runs or trusts the damaged payload:
piship repair acmecode ./acmecode-1.1.0-darwin-arm64.tar.gz # or a release directory or payloadThe source must be the same release that is installed: it is verified like an install (verify-release for this target for an archive or release directory, or the payload inventory and lock for a payload directory), and must have a version this installation records (the active or the retained release), the same command, and, when the receipt recorded them at install or update, the same lock digest and archive digest. A copy from another build of that version, or of another version, is refused with nothing changed. A release that verifies is reported intact and left alone; a release a running session still uses is refused until the session exits. Otherwise the damaged payload is replaced by the verified copy in two renames inside apps/<id>; the receipt, state, and credentials are untouched. An interruption between the renames leaves the release missing, which the launcher refuses, and running the same repair again restores it. Afterwards update and rollback work again. piship here is the PiShip CLI; a user without one can use the copy inside the extracted release they downloaded, node <release-dir>/payload/piship.mjs repair <id> <release-dir>, when that release was built with a PiShip that has repair (later than 0.7.0). Never run the piship.mjs of the damaged payload.
Removing an unexpected file from the payload by hand also restores it, when the message lists only unexpected files. A modified or missing file needs repair.
The store (architecture) is on by default on Windows (hardlink) and off on macOS and Linux unless PISHIP_STORE names a placement mode (PISHIP_STORE=off turns it off anywhere), and an installation never depends on it, so it is safe to delete: remove the directory PISHIP_STORE_HOME names, or store in the PiShip cache directory (%LOCALAPPDATA%\piship\store on Windows). The next install or update that uses it fills it again; nothing installed changes. doctor collects the objects that no active or rollback release pins and removes a damaged object, and it says so on stderr; it does not touch the installed payload, which it verifies separately. An install that cannot use the store (it is not writable, it is inside the state, install, or bin home, or the volume cannot clone) writes the files itself and succeeds; one that finds PISHIP_STORE set to an unknown value stops with CONFIG_INVALID. After a hard-linked install (the default on Windows, or PISHIP_STORE=hardlink), an installed file is a second name for a store object, so a program that changed its mode and wrote to it changed the object as well: doctor reports the installed release as damaged, and piship repair restores it from a trusted archive as it does any damaged payload.