Skip to content

Debugging & troubleshooting

Most issues come down to one of three things: the machine isn't connected, it hasn't synced a bundle yet, or the hook isn't wired correctly. This page maps the exact message you'll see to its fix. Start by running skillsvault status — it shows the connected org, the bundle version, and how long ago it synced.

Quick reference

SymptomCauseFix
not configured — run skillsvault loginNo credentials on this machineskillsvault login (or skillsvault configure for CI)
no bundle — run skillsvault syncConnected, but the first sync failedskillsvault sync
REJECTED bundle: signature verification failedPinned org key no longer matchesReconnect to re-pin: skillsvault login
Gate exits 2 unexpectedlySkill is banned, or a Block policy matches itskillsvault gate --skill <name> shows the policy and reason; review your policies
Hook never blocks in Claude CodeHook config missing or out of datePaste the output of skillsvault install-hook into your Claude Code settings
⚠ the gate hook doesn't cover subagents or typed /commands …Hook from before CLI 0.5.1 (matcher "Skill" only)Paste skillsvault install-hook's output over the old entry, then skillsvault sync
⚠ no skillsvault gate hook is installed … (from detect)Claude Code is installed but nothing runs the gatePaste skillsvault install-hook's output into your Claude Code settings
Claude Code shows skillsvault: not configured on this machine … or … no policy bundle …The hook ran with nothing to decide with, so it let the call throughskillsvault login, or skillsvault sync
WARN … policy bundle is N hours old — run skillsvault syncThe bundle is older than the staleness limit (24 hours — a skillsvault default set in the bundle) and the gate's background refresh has been failing for as longskillsvault sync shows why it fails
⚠ 1 audit event queued locally …The control plane didn't answer within 1.5 secondsNothing to do: the next gate, sync or pull sends it
⚠ skillsvault 0.x.y is out of dateA newer CLI is releasedRe-run curl -fsSL https://skillsvault.io/install.sh | sh
A banned command only warnsCommands are capped at warnExpected — see the gate
HTTP 401Bad or missing agent keyCheck the key in Settings → API keys
HTTP 403Key used against another orgUse the key's own org slug
no harnesses detected on this machinepull found no known agent dirsPass --harness <h> or --dir <path>

Connection & sync

not configured

The CLI keeps per-machine state under ~/.skillsvault. If it's missing you'll see not configured — run skillsvault login. Connect interactively with skillsvault login, or non-interactively (CI) with skillsvault configure --org <slug> --key <agent-key>.

no bundle — run skillsvault sync

The gate decides from a locally-synced bundle. login and configure sync one as soon as the machine is connected; if that first sync failed (they print could not sync yet), run skillsvault sync, which pulls and verifies it. Confirm with skillsvault status:

skillsvault status
# bundle    v1750848000 · 4 skills · 3 policies
# synced    2m ago

REJECTED bundle: signature verification failed

On connect, the CLI pins the deployment's bundle-signing key (one key for the whole skillsvault deployment, the same for every organisation) and then refuses any bundle that doesn't verify against it. If that key was rotated, an old pin will reject the new bundle. Reconnect to re-pin the current key:

skillsvault login        # re-pins the current signing key
skillsvault sync

The last-good bundle is kept on a rejected sync, so the gate keeps working on the previous policy until you re-pin.

Enforcement

Gate exits 2 when you didn't expect it

Exit 2 is a block. It means the skill is banned, or an active Block policy matches it — a Specific skill, namespace or target policy. A bare name like deploy is checked against every artifact of that name in your registry, and the most restrictive decision wins. Review the matching rule in Policies. To see the decision and reason directly:

skillsvault gate --skill acme/teamleader-crm-entry
# ⛔ BLOCKED acme/teamleader-crm-entry
#    policy: Teamleader retired
#    Use acme/hubspot-crm-entry instead.

The Claude Code hook isn't blocking

Check, in order:

  1. The CLI is current. Below 0.5.1 the gate misses bare names, subagents and typed commands. Run skillsvault version; upgrade as in Upgrading an existing machine.
  2. The hook config is current. The PreToolUse matcher must be "Skill|Agent|Task" (a "Skill"-only matcher never sees subagents), and a UserPromptExpansion hook must run the gate too, or a typed /name is never checked. Print the correct config with skillsvault install-hook and paste it into ~/.claude/settings.json or the project's .claude/settings.json. skillsvault sync then reports the coverage; the console shows Gating: full once every path is covered. See Harness coverage.
  3. A bundle is synced and current. With no bundle the gate can't decide, so it lets the call through and Claude Code shows a skillsvault: … message saying so. After a ban, the machine enforces it from its next skillsvault sync (or try).
  4. The skill was installed by pull. Claude Code invokes a subagent by its frontmatter name; the gate learns that name when pull installs it. Re-run skillsvault pull --all after upgrading the CLI.
  5. It isn't a command. A ban against a slash command warns instead of blocking — see the gate.
  6. You're reading the right signal. In hook mode the gate exits 0 and signals a block via permissionDecision: "deny" (or "decision": "block" for a typed command) in its JSON output — not via the exit code. That's by design; see The runtime gate.

Enforcement is cooperative

The gate stops the accidental run of a stale or banned skill. It does not stop a developer who removes the hook or sideloads a folder outside a managed namespace. If a banned skill still runs, confirm the hook is installed and the namespace is managed.

API errors

When using the REST API or skillsvault against a remote control plane:

  • 401 — the agent key is missing or unknown. Confirm the X-Skillsvault-Key (or Authorization: Bearer) header and that the key still exists in Settings → API keys.
  • 403 — the key is valid but you're calling a different org's path than the key belongs to. Use the key's own org slug.

Offline & staleness

The gate never needs the network to decide — it uses the last-good bundle on disk, so an offline laptop keeps enforcing whatever policy it last synced, and a slow control plane never delays a decision. The audit event for a decision made offline is queued in ~/.skillsvault/audit-spool.jsonl (capped at 1 MB) and sent by the next gate, sync or pull that reaches the control plane; it is recorded with the time it arrived, not the time of the decision.

The gate keeps the bundle current on its own: once it is an hour old, the next gate starts a background sync after answering (at most one every five minutes). If that keeps failing — the machine is offline, the key was revoked — and the bundle is older than the staleness limit (24 hours, a skillsvault default set in every bundle) with refreshes failing for that long, every skill it would have allowed gets a warning — policy bundle is 30 hours old — run skillsvault sync — and blocks stay blocks. Staleness alone never blocks: the stale action in the bundle is warn. Run skillsvault sync by hand to see why refreshing fails, and skillsvault status for freshness. A machine signed out with logout --keep-bundle can't refresh, so its bundle's age alone decides. (CLI 0.6.0 and later; older versions enforce an old bundle without saying so and drop offline audit events.)

Inspecting state

skillsvault status              # org, bundle version, sync age
skillsvault status --json       # machine-readable, for scripts
skillsvault detect              # AI agents found, and whether the gate hook covers Claude Code
skillsvault gate --skill <name> # a real decision with full reason — recorded in the audit log

gate --skill is not a dry run: it records an audit event like any other gate call, and a block or warning goes to any alert destination subscribed to it. To see what the gate would decide without recording it in the audit log, use the console's Playground, which evaluates a registry skill against your org's current policies.

Add --no-color (or set NO_COLOR=1 / TERM=dumb) when capturing output, so logs and pipes stay plain text.