Help
Troubleshooting
On this page
Problems are grouped by stage. Find the message you saw, apply the fix, then run the confirm command.
Some messages come from the layer under Brig: the microVM runtime (hull on macOS, nerdctl on Linux), cosign, or Homebrew. Each entry says so.
Start with brig doctor #
brig doctorIt checks the host, the hypervisor, the runtime and its version, the boot assets, cosign, the profiles, the secret store and brigd. Each check prints one line. A line that is not ok names its fix.
| Command | What it adds |
|---|---|
brig doctor <agent> |
Checks that agent's image too. |
brig doctor --json |
Prints the same report as one document, for a script or a bug report. |
Exit codes #
The table of exit codes is in CLI.
brig rm and brig logs on a ref with no sandbox exit 3, not the general failure code 1. They name the ref you typed.
Checks brig doctor misses #
Only two lines in brig doctor change its exit status. They use the codes a run exits with. Every other line, a !! line included, prints its fix and leaves the exit status at 0.
Warning A script that reads only the exit status of
brig doctormisses verification problems. Read the lines too.
Two verification problems print !!, exit 0, and still stop a real run:
- A
BRIG_VERIFYvalue Brig does not recognize. BRIG_VERIFY=requirewith no cosign installed.
Print the setting before you rely on it:
echo "$BRIG_VERIFY"require also refuses any image outside Brig's registry, ghcr.io/brig-sh/. claude-desktop and ubuntu are both outside it. brig doctor claude-desktop reports that image as --, informational, for every BRIG_VERIFY value. It does not show the refusal that a require run gets.
brig doctor also does not read the runtimeBin: field of a profile. See Profile runtimeBin is missing.
Install and runtime #
Homebrew rejects brew trust #
Error: Unknown command: trustCause. The message comes from Homebrew. An older Homebrew does not have brew trust.
Fix. Update Homebrew:
brew --version
brew updateConfirm.
brew trust brig-sh/brigThe command runs without the error.
No runtime on PATH #
brig: no runtime found on PATH: brig drives hull on macOS, and none was there.
See https://github.com/brig-sh/brig#macos, or point BRIG_RUNTIME_BIN at a buildbrig: no runtime found on PATH: install nerdctl, or point BRIG_RUNTIME_BIN at oneThe exit code is 4 on both platforms.
Cause. Brig does not ship the runtime that boots a sandbox, and found none on PATH. On macOS the cask depends on hull, so the usual cause is a from-source install.
Fix. Look for the runtime, then install it. See Install.
which hullThe cask installs hull:
brew install --cask brigwhich nerdctlInstall nerdctl.
If you have a build that is not on PATH, point Brig at it:
BRIG_RUNTIME_BIN=/path/to/hull brig run claudeConfirm.
brig doctorThe runtime line reads ok and names the binary it found.
Unknown BRIG_RUNTIME value #
brig: runtime unavailable: unknown BRIG_RUNTIME "podman" (want hull or nerdctl)Cause. BRIG_RUNTIME names a runtime that Brig does not drive. brig ls and brig info fail with the same message.
Fix. Print the value:
echo "$BRIG_RUNTIME"Set it to hull or nerdctl. To make Brig look on PATH, unset it:
unset BRIG_RUNTIMEConfirm.
brig doctorThe runtime line reads ok.
Profile runtimeBin is missing #
brig: runtime unavailable: this profile's runtimeBin is /old/path/hull, which is
not there: stat /old/path/hull: no such file or directoryCause. The runtimeBin: field of your profile names a binary that moved or was removed. The runtime line of brig doctor reads only BRIG_RUNTIME_BIN, so it still reads ok.
Fix. Open the profile and correct or remove the line:
brig agent edit mineConfirm.
brig info mineThe output shows no runtime error.
Docker drops the kernel annotation #
This error occurs on Linux only, for a profile that boots an unmodified image. Six of the eight built-in profiles do.
brig: could not start the sandbox: this profile boots an unmodified image,
which needs the kernel passed as an OCI annotation; docker does not carry
annotations through to the runtime. Use nerdctl, or point BRIG_RUNTIME_BIN at
itCause. Brig accepts Docker in place of nerdctl. Docker drops the OCI annotation that carries the kernel, so the guest has no kernel to boot.
Fix. Install nerdctl, or point Brig at one you already have:
BRIG_RUNTIME_BIN=/path/to/nerdctl brig run claudeConfirm.
brig doctorThe runtime line names nerdctl.
hvi needs macOS 15 #
brig: the hvi hypervisor needs macOS 15 or newer (this is 14.5): its in-kernel
interrupt controller does not exist here. Set BRIG_HYPERVISOR=vz BRIG_NETWORK=shared
for this run, or upgrade macOSCause. Six of the eight built-in profiles use the hvi hypervisor backend. It needs Apple's in-kernel interrupt controller (the hv_gic_* calls), which arrived in macOS 15.
Fix. For one run, use the vz backend (Virtualization.framework):
BRIG_HYPERVISOR=vz brig run claude --network sharedThe built-in hvi profiles name network: isolated, which vz cannot provide. This command chooses a shared network, where sandboxes are not promised separation.
For later runs, choose one:
- Put both
BRIG_HYPERVISOR=vzandBRIG_NETWORK=sharedin your shell profile. - Upgrade to macOS 15 or newer and keep
hvi.
Confirm.
brig run claudeThe run reaches the agent's prompt.
First boot #
Boot assets missing #
This line appears in the output of brig doctor:
!! boot assets missing at /Users/alex/.hull/store/assets
run any agent once to fetch them, or set BRIG_BOOT_ASSETS to a directory that has themCause. Six of the eight built-in profiles boot an unmodified OCI image with a shared kernel and initrd, the boot assets. Brig did not download them yet.
Fix. No action is necessary. The line is normal before a first boot and does not stop one. The next brig run on one of those profiles fetches the boot assets.
Confirm.
brig doctorThe boot line reads ok boot assets present at /Users/alex/.hull/store/assets.
Image pull or architecture failure #
brig: could not start the sandbox: <runtime error>Cause. The detail after the colon comes from the runtime. It is one of these:
| Cause | Fix |
|---|---|
| A registry Brig cannot reach | Try again once the registry is reachable. |
| An image reference that does not exist | Check the reference with brig info claude. |
| A manifest with no build for your architecture | Drop the pinned tag, or pin the one for your machine (:arm64 or :amd64). |
The five published agents default to :root. brig-sh/community-images publishes it as a multi-arch index for linux/arm64 and linux/amd64. A mismatch usually means that --image or BRIG_IMAGE pins a single-architecture tag that is not yours.
Fix. Print the image reference:
brig info claudeRead the runtime's error to find the cause. Then apply the fix from the table.
Under the default pull policy, Brig does not fetch a moving tag that was republished. To fetch it:
BRIG_PULL=always brig run claudeBrig does not publish some images, such as cursor. Brig says so before it reaches the registry. Build the image yourself and pass --image.
Confirm.
brig run claudeThe sandbox boots.
Sandbox never became ready #
brig: sandbox did not become ready; check 'brig logs claude (or the runtime's own, hull logs brig-claude-code)'On Linux the second half names nerdctl logs.
Cause. The runtime reported the sandbox as running, but the agent inside it never answered. Brig waited for the guest to bind its listener, and the wait timed out.
Fix. Read the log. The guest's errors are there.
brig logs claudeIf the boot never became a sandbox that Brig can address by ref, use the runtime command that the message names.
If the guest is slow, set BRIG_READY_TIMEOUT in seconds. The default is 30.
BRIG_READY_TIMEOUT=60 brig run claudeLinux hosts with their own urunc
On Linux, a genericBoot profile also fails this way when the host runs a urunc release. No release reads the boot annotations Brig passes. brig doctor still reports the runtime and the boot assets as ok.
A host has its own urunc in one of two cases:
- It installed with
BRIG_INSTALL_RUNTIME=0. - It ran an
install.shfrom Brig 0.2.0 or earlier, which installed no runtime on Linux.
Install the runtime bundle, which carries a urunc that reads the annotations. See Runtimes.
curl -fsSL https://brig.sh/install | shConfirm.
brig run claudeThe run reaches the agent.
Images and verification #
cosign is not installed #
The message depends on BRIG_VERIFY.
Under BRIG_VERIFY=warn, the default:
brig: cannot verify image ghcr.io/brig-sh/claude-code-stock:root: cosign is not installed (`brew install cosign`)
↳ booting it uncheckedUnder BRIG_VERIFY=require nothing boots, and the exit code is 5:
brig: refusing to boot image ghcr.io/brig-sh/claude-code-stock:root: cosign is
not installed (`brew install cosign`), so nothing could be checked
(BRIG_VERIFY=require). Install cosign, or set BRIG_VERIFY=warn to boot it
uncheckedUnder BRIG_VERIFY=off Brig never looks for cosign, and the only line is:
brig: BRIG_VERIFY=off, so the signature and digest checks are skipped: the
guest image and the kernel it boots are not checkedFor a profile that boots its own image, Brig has no kernel to skip, so the off line names the image alone.
Cause. Brig found no cosign to run the check. Under warn it boots the image unchecked. Under require it refuses every image, Brig's own included.
Fix. Install cosign.
brew install cosignNo package covers every distro. Install a release from sigstore/cosign and put it on PATH.
To boot without the check and stop the warning, set BRIG_VERIFY=off. It is safer to install cosign.
Confirm.
cosign versioncosign prints its version. The next run under BRIG_VERIFY=warn or require checks the image.
Registry unreachable during verification #
brig: cannot reach the registry to verify image ghcr.io/brig-sh/claude-code-stock:root: <detail>
↳ the copy on disk could not be checked against what the registry serves
brig: Boot the cached copy unverified? [y/N]Cause. The registry did not answer, so Brig did not check the copy on disk. The usual cause is no network, or a captive portal that answers every host with its own page.
If you answer no, Brig aborts:
brig: aborted: the registry could not be reached, so the image could not be
verified. Try again with the registry reachable, or set BRIG_VERIFY=off to
boot the cached copy uncheckedUnder BRIG_VERIFY=require there is no prompt. Brig refuses, with the same exit code, 5.
Fix. Reconnect and run again. If you trust the copy on disk, answer y to boot it once unverified.
Confirm.
brig run claudeThe run reaches signature verified with no prompt.
cosign did not answer #
brig: cannot verify image ghcr.io/brig-sh/claude-code-stock:root: cosign did
not answer within 30s. It waits on docker-credential-desktop, set by
credsStore in /Users/you/.docker/config.json. Start the app that helper
belongs to, or run brig with DOCKER_CONFIG set to an empty directory (and
restart brigd with it set, if brigd is running)
↳ the copy on disk was not checked against what the registry serves
brig: Boot the cached copy unverified? [y/N]Cause. The usual cause is a Docker credential helper that never answers. cosign reads Docker's config.json and runs the helper that credsStore or credHelpers names for ghcr.io. With "credsStore": "desktop" and Docker Desktop not running, docker-credential-desktop blocks.
The message names the helper and the file only when one is set. Without one, it goes from "within 30s" straight to "The copy on disk was not checked". Then the causes in Registry unreachable during verification apply.
After 30 seconds Brig kills the process group of cosign, which includes a credential helper that cosign started. Brig does not kill a helper that left the group with setsid.
If you answer no, Brig aborts, and the error repeats the detail:
brig: aborted: the image was not verified: cosign did not answer within 30s.
<detail>. Try again once cosign answers, or set BRIG_VERIFY=off to boot it
uncheckedUnder BRIG_VERIFY=require there is no prompt. Brig refuses, with exit code 5.
Fix. The images Brig boots are public, so cosign needs no credentials for them. Start Docker Desktop, or point DOCKER_CONFIG at an empty directory for the run:
DOCKER_CONFIG="$(mktemp -d)" brig run claudeIf brigd runs your boots, stop it and start it again with DOCKER_CONFIG set.
Confirm.
brig run claudeWithin a few seconds, the run prints brig: image and boot assets verified with no prompt.
Signature did not verify #
brig: image ghcr.io/brig-sh/claude-code-stock:root claims to be published by
brig-sh, but its signature DID NOT VERIFY: <detail>
brig: Boot it anyway? [y/N]Cause. cosign ran, and the check failed. The image is under Brig's registry, and its signature does not match the workflow that builds those images.
With no terminal to ask, Brig refuses:
brig: not a terminal, so there is nobody to ask: refusing
→ to boot it regardless: BRIG_VERIFY=offIf you answer no, Brig aborts, with exit code 5:
brig: aborted: the image failed verification. Pull it again (BRIG_PULL=always),
or set BRIG_IMAGE to a digest you have checked yourselfFor an image from another publisher, Brig warns and continues. Only a failure under Brig's registry stops the run.
Fix. The usual harmless cause is a stale local copy. Pull the image again:
BRIG_PULL=always brig run claudeWarning If it still fails and you do not know why, do not boot it. To get past the check, name a digest that you checked yourself.
Confirm.
brig run claudeThe run reaches signature verified and boots.
Boot assets fail verification #
brig: refusing to boot: the boot assets in ~/.hull/store/assets are not the
bundle that verified, ghcr.io/nofireai/hull-assets:darwin-arm64
(sha256:e82a...): container-initrd is sha256:55d2..., not the sha256:05cb...
it lists. Delete both files there and run again to fetch the bundle, or set
BRIG_BOOT_ASSETS to that directory if they are your own buildCause. The kernel and initrd on disk are not the files that the signed bundle lists. Either something changed a file after the fetch, or a Brig or hull that kept no record fetched the files. Brig refuses under warn as well as require, with exit 5.
Files from an older bundle that the record beside them names do not cause this refusal. Brig fetches that bundle again, and the run says so.
Fix. Pick the row that matches your files:
| Cause | Fix |
|---|---|
| The files are not your own build | Delete the two files the line names and run again. Brig fetches the bundle whose signature it checked, and the next run compares that. |
| The files are your own build | Point BRIG_BOOT_ASSETS at their directory. warn then states the difference and boots them. |
See Security.
Runtime bundle kernel mismatch #
This error occurs on Linux.
brig: refusing to boot: the kernel and initrd in
/var/lib/brig/data/share/guest are not the ones the Linux runtime bundle's
signed record lists: bzImage is sha256:9c1e..., not the sha256:4f0a... it
lists. Re-run brig's install.sh to reinstall the bundle, or point
BRIG_BOOT_ASSETS at a directory of your own buildCause. The Linux runtime bundle carries the kernel and initrd, and its release signs a record of their digests. A file in the bundle's share/guest changed after the install and no longer matches the record. Brig refuses under warn as well as require, with exit 5.
The same refusal names two other cases:
- A record the release's
checksums.txtdoes not list. - A
checksums.txtwhose signature does not verify.
Fix. Pick the row that matches your host:
| Cause | Fix |
|---|---|
| The bundle's files were changed | Run Brig's install.sh again to put the bundle's files back. |
| The kernel is your own build | Keep it in a directory of its own and point BRIG_BOOT_ASSETS there. |
| The bundle was released from a fork | Its signature names the fork's release workflow. Set BRIG_VERIFY_RUNTIME_IDENTITY to it. |
See Security.
Credentials #
Secret store could not be read #
brig: the mine sandbox needs gh-token from brig's secret store, which could
not be read: <cause>Cause. The secret store did not answer, so Brig cannot tell whether the value is there. The run exits with code 6, the same code as a missing secret.
Fix. Run brig doctor:
brig doctorThe secrets line names the failure. Fix it, then run again:
| Cause | Fix |
|---|---|
| A locked keychain | Open the keychain. |
| A keyring that is not running | Start the keyring. |
| A missing permission | Grant the permission. |
Confirm.
brig doctorThe secrets line reads ok secrets keychain reachable, or names your platform's store.
Required secret is missing #
brig: missing secret "gh-token" needed by the mine sandbox -- create it
first with: brig secret create gh-tokenCause. A secret that a profile declares required: true has no value in Brig's secret store. The run exits with code 6. No built-in profile has a required secret, so the declaration is in your own profile (brig agent edit mine).
If two secrets are missing, the message lists one line for each.
Fix. Create the secret that the message names:
brig secret create gh-tokenFor a secret that the profile marks importable, the message names brig secret import <profile>. That command copies the value from your host.
Confirm.
brig info mineThe secret does not show as missing, and its name appears in the CREDENTIALS row.
Credential did not arrive #
List what Brig forwards, by name:
brig info claudeThe output shows what reaches the guest and whether the guest will be authenticated. If the variable you expected is not listed, one of the causes below applies.
The variable is on the denylist #
brig: not forwarding ANTHROPIC_API_KEY: it is on the claude-code denylist
↳ it outranks the subscription credential, and would move this sandbox onto metered billing without saying so
→ to forward it anyway: BRIG_ALLOW_DENIED=1Cause. By default, Brig does not forward a key that moves the sandbox from your subscription to metered billing.
Fix. If you want metered billing, set BRIG_ALLOW_DENIED=1.
The value is an unresolved reference #
brig: not forwarding GH_TOKEN: it looks like an unresolved secret reference (op://...), not a credential
→ resolve it on the host before you run brig
→ to forward it as it is: BRIG_ALLOW_REFS=1Cause. Tools such as direnv leave a scheme:// value in the environment when they do not resolve a secret-manager reference. If Brig forwards it, the guest reports "Invalid username or token".
This check does not apply to a stored secret or a profile literal.
Fix. Resolve the reference on the host so that the variable holds the real token. Then run again.
The variable is empty #
Cause. Brig skips an unset or empty variable, so that it cannot shadow a value in the image.
The stored credential expired #
brig: the imported credential claude-credentials (claude-code) expired 3d ago
→ renew it on the host, then: brig secret import claude-codeCause. Brig forwards an expired stored credential as it is, and warns before boot.
Fix. Renew the login on the host. Then import it again:
brig secret import claude-codeA run reads only Brig's stored copy, so the import is necessary.
For a secret that you stored with --from-command, the second line names that command:
brig: the imported credential <name> (claude-code) expired 3d ago
→ renew it, then store it again: brig secret import claude-code <name> --from-command '<command>'Confirm the credential arrives #
brig info claudeThe credential appears in the list of what Brig forwards, and the warning is gone.
Agent asks to log in again #
There is no error. The agent shows its login screen on a sandbox where you logged in before.
Cause. On claude-code and claude-desktop, the in-guest login is on a memory-backed mount that never reaches host disk. brig stop removes it with the microVM. The other six profiles mount the guest home from host disk, so a login there survives a stop.
Fix. Import the login from this Mac into Brig's secret store, once:
brig secret import claude-codeBrig then delivers the login on every command that reaches the sandbox. See Authentication.
Confirm.
brig stop claude
brig run claudeThe agent starts logged in.
Sessions and workspaces #
Sandbox restarted on brig sh #
Cause. Brig restarts the sandbox in the four cases below. All persistent state is in the guest home on the host. See Sessions.
Note Any other session on that sandbox is disconnected when it restarts.
A different guest home #
brig: the running sandbox is not mounting /Users/alex/work: its share went stale
↳ the directory was renamed or replaced, or the workspace changed
↳ brig restarts it, and any other session using this sandbox will be disconnectedAn explicit --home or BRIG_WORKSPACE does not match the guest home that the running sandbox has.
The WORKSPACE column shows the guest home that a sandbox mounts:
brig lsIf you did not intend to change the guest home, remove the --home flag.
A network policy that changed #
brig: this sandbox is running under a different network policy than the one that applies now
↳ rules are fixed when a sandbox boots, so brig restarts it
↳ any other session using this sandbox will be disconnectedEgress rules are fixed at boot. If you attach or detach a policy while a sandbox runs, the next brig sh or brig run on that session restarts it.
Show the policy that a profile has bound, and whether Brig can enforce it:
brig policy check claudeA different posture #
brig: this sandbox was started with the isolated posture and --network asks for shared
↳ rules are fixed when a sandbox boots, so brig restarts it
↳ any other session using this sandbox will be disconnected--network or BRIG_NETWORK names a posture that differs from the one the sandbox started with. A command that names no posture does not cause a restart. If you did not intend to change the posture, look for an exported BRIG_NETWORK in this shell.
The warning names the posture that the sandbox runs with. For a sandbox that a policy isolated, the name is isolated, even after you detach the policy.
A different project #
brig: the running sandbox has /Users/alex/app mounted as its project and this run names /Users/alex/other-app
↳ a share cannot be attached to a live sandbox, so brig restarts it
↳ any other session using this sandbox will be disconnectedThe directory on the run line differs from the project that the session last used. A project is a share, fixed at boot.
The PROJECT row, when there is one, names the project that the session last used:
brig info claudeTo keep the sandbox up, pass the same directory, or none.
Confirm the sandbox stays up #
brig sh claudebrig ls lists the ref as running, and a second brig sh on it prints no restart warning.
Symlinked guest home refused #
brig: refusing to write /Users/alex/brig/claude-code/.claude.json: it is a
symlink to "/Users/alex/.ssh/authorized_keys", and brig writes only regular
files inside the workspace. The workspace is mounted read-write as the
sandbox's home, so that link was put there from inside the sandbox, to have
brig -- which runs as you, on the host -- reach a file the sandbox cannot.
Nothing was written; inspect /Users/alex/brig/claude-code/.claude.json and
remove it before running brig again: a symlink leads out of a directory brig
is checkingCause. Brig writes state files into the guest home from the host, as you. A symlink in place of one of those files points Brig at a host path that the sandbox cannot reach. Brig does not follow the link and writes nothing.
Fix. Do not retry. Inspect the path that the message names. Then remove the link, or point the guest home at another directory.
Brig refuses a --home that is a symlink in the same way. Name the real directory.
Confirm.
brig run claudeThe run reaches the agent. See Security.
Project behind a symlink refused #
brig: refusing to use /Users/alex/work/app as this run's project:
/Users/alex/work on the way to it is a symlink to "/Volumes/data/work", so the
sandbox would be handed a directory other than the one you named. Name the
real directory instead: a symlink leads out of a directory brig is checkingCause. The project is mounted read-write, so the sandbox can replace any directory at or below it with a link. Brig cannot tell that link from one you made, so it refuses both and names the link target.
Brig still follows a link in a directory that you cannot write, such as /tmp on macOS.
Fix. Name the real directory:
brig run claude /Volumes/data/work/appIf the message ends with "This session's project was remembered from an earlier run", the project came from an earlier brig run of this session. Replace it in one of two ways:
brig runwith the real directory after the ref.brig runwith--no-project.
Until then, Brig refuses only the verbs that boot or join the sandbox. brig stop, brig rm and brig info still work, and brig info repeats the refusal.
Confirm.
brig info claudeThe PROJECT row names the real directory, and the refusal is gone. See Security.