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
| Symptom | Cause | Fix |
|---|---|---|
not configured — run skillsvault login | No credentials on this machine | skillsvault login (or skillsvault configure for CI) |
no bundle — run skillsvault sync | Connected, but the first sync failed | skillsvault sync |
REJECTED bundle: signature verification failed | Pinned org key no longer matches | Reconnect to re-pin: skillsvault login |
Gate exits 2 unexpectedly | Skill is banned, or a Block policy matches it | skillsvault gate --skill <name> shows the policy and reason; review your policies |
| Hook never blocks in Claude Code | Hook config missing or out of date | Paste 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 gate | Paste 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 through | skillsvault login, or skillsvault sync |
WARN … policy bundle is N hours old — run skillsvault sync | The 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 long | skillsvault sync shows why it fails |
⚠ 1 audit event queued locally … | The control plane didn't answer within 1.5 seconds | Nothing to do: the next gate, sync or pull sends it |
⚠ skillsvault 0.x.y is out of date | A newer CLI is released | Re-run curl -fsSL https://skillsvault.io/install.sh | sh |
| A banned command only warns | Commands are capped at warn | Expected — see the gate |
HTTP 401 | Bad or missing agent key | Check the key in Settings → API keys |
HTTP 403 | Key used against another org | Use the key's own org slug |
no harnesses detected on this machine | pull found no known agent dirs | Pass --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:
- 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. - The hook config is current. The
PreToolUsematcher must be"Skill|Agent|Task"(a"Skill"-only matcher never sees subagents), and aUserPromptExpansionhook must run the gate too, or a typed/nameis never checked. Print the correct config withskillsvault install-hookand paste it into~/.claude/settings.jsonor the project's.claude/settings.json.skillsvault syncthen reports the coverage; the console shows Gating: full once every path is covered. See Harness coverage. - 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 nextskillsvault sync(ortry). - The skill was installed by
pull. Claude Code invokes a subagent by its frontmattername; the gate learns that name whenpullinstalls it. Re-runskillsvault pull --allafter upgrading the CLI. - It isn't a command. A ban against a slash command warns instead of blocking — see the gate.
- You're reading the right signal. In hook mode the gate exits
0and signals a block viapermissionDecision: "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 theX-Skillsvault-Key(orAuthorization: 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.