Troubleshooting
Check what is configured
Start here. It answers most of the questions below without guessing:
vulnetix ai-firewall status
It reports which clients are wired, which are pointing somewhere else, which providers have keys, and every conflict between your local config and the organisation’s policy. Each finding names the 403 it would produce at runtime.
Authentication
401 Unauthorized— the gateway does not know who you are. Almost always the two-key confusion: the client is sending the provider’s key, when the gateway wants your Vulnetix key. The provider key lives server-side; your machine should never hold it.- Check
$VULNETIX_API_KEYis exported and is what the client’s key variable resolves to. - For Anthropic:
ANTHROPIC_API_KEYis sent asx-api-keyand the gateway never reads it. You needANTHROPIC_AUTH_TOKEN, which producesAuthorization: Bearer. This failure looks exactly like a bad key, and checking the key will not fix it.
- Check
403with acode— you authenticated fine; policy refused the request. The code says which stage. See block responses.
Requests are not going through the gateway
The quiet failure, and the one worth hunting: nothing errors, and nothing is screened.
statussayspoints elsewhere. That client has a base URL, and it is not ours. Re-runvulnetix ai-firewall install <client>.- The shell block was written but never sourced.
statuschecks the process environment, not the rc file — because that is what the SDK sees. Start a new shell, orsource ~/.zshrc. - The SDK does not read the variable you set. Only
OPENAI_BASE_URL,OPENAI_API_BASE, andANTHROPIC_BASE_URLare honoured by real SDKs.MISTRAL_BASE_URLand friends do not exist — no SDK reads them, and the CLI does not write them. For those providers you must setbase_urlin code: snippets. - You are using the Vercel AI SDK. It ignores
OPENAI_BASE_URLentirely. It needscreateOpenAI({ baseURL })—vulnetix ai-firewall snippet --lang ts --sdk vercel-ai. - The
.envis not being expanded.${VULNETIX_API_KEY}expands under python-dotenv and direnv, but plain Nodedotenvdoes not expand it withoutdotenv-expand. Export the variable in the shell instead.
Blocked when you did not expect it
model_not_allowedon a model nobody denied. Someone allowed a different model for that provider, which put the provider into allowlist mode. Every unlisted model is now refused. Add yours to the list, or remove the allow entries.request_blockednaming a rule you did not write. You havespec.baseline.enabled: trueand a baseline guardrail matched.vulnetix ai-firewall baselinelists them; exclude it byidif it is wrong for you.- A redaction is mangling legitimate prompts. Switch the rule to
--action flagfor a while and read the logs. A pattern that looks obviously safe usually is not.
Not blocked when you did expect it
Worse than the above, because nothing tells you.
- The pattern does not compile, so the gateway skips the rule. It sits in the dashboard looking enforced and enforces nothing.
vulnetix ai-firewall statuscompiles every pattern and warns. The usual cause is lookaround: RE2 has none. Rewrite(?<=orgUuid=)\S+asorgUuid=\S+, which blocks the same requests. - The guardrail is disabled.
enabled: false, or--disable. - A redaction ran first. Guardrails evaluate in ascending priority. A
redactrule at priority 10 rewrites the text before ablockrule at priority 20 sees it, so the block may no longer match. Give the block the lower number. - The client was never wired. See the section above — an unwired client’s traffic never reaches a guardrail at all.
Agents
- Codex. Its
wire_apionly acceptsresponses, so it cannot use a chat-completions-only gateway. If the gateway does not serve the Responses API for the provider,installskips Codex and says so — it will not write a config that 404s on first use. - Claude Code. It speaks the Anthropic Messages API (
/v1/messages), not chat completions. Same gate. Also: its token is not written into.claude/settings.json(that file gets committed), so it must come from your shell — or fromsettings.local.jsonwith--embed-key. - Continue. It is an IDE extension and cannot read your shell environment. It resolves
${{ secrets.VULNETIX_API_KEY }}from~/.continue/.env, which is why that file is the one place the CLI writes a literal key. - Cursor / Windsurf. No config file to write; the base URL lives in application state and you must paste it into their settings. Their integrated terminals do inherit the shell block, so
aiderandcodexrun inside them are already wired.
Policy files
- “declares metadata.org X, but you are authenticated as Y” — you are about to apply one organisation’s policy to another. That guard exists because it is the easiest way to do real damage. Check before reaching for
--force. - “duplicate name” — guardrails are reconciled by name, so names must be unique. Rename one, or manage it by
--uuidwithpolicy guardrail. applydeleted nothing you expected it to. Deletion is opt-in: unmanaged objects are reported as drift. Pass--prune, after a--dry-run.- CI applied a policy with no baseline guardrails and went green. Add
--baseline-required. Outside CI an unavailable baseline is a soft failure by design; in CI that is exactly the wrong behaviour.
Start over
vulnetix ai-firewall uninstall --all --dry-run # check first
vulnetix ai-firewall uninstall --all
vulnetix ai-firewall install
Uninstall touches local files only — your policy, guardrails, and stored provider keys are untouched, and it needs no authentication.