Troubleshooting
Errors you're likely to hit, what causes them, and how to fix them, followed by the current list of known limitations.
fitguard run prints warnings for config problems that don't stop it booting but change how it behaves, such as a key mapped to a user that doesn't exist. Most reports of budgets not working trace back to one of those warnings.
Startup problems
config must define at least one provider
Your config.yaml has nothing under providers:. Run fitguard init, or add a provider block by hand. FitGuard refuses to start with no providers rather than booting into a state where every request fails.
address already in use
Something already holds that port, often another fitguard run you forgot about. Find it with lsof -i :8787, or change port: in your config.
connecting to redis: ...
You set a Redis backend but FitGuard can't reach it. Check Redis is up and that the URL and credentials are right. This is a hard failure by design, since falling back to in-memory would silently return you to per-process budget tracking, which is the situation Redis was configured to avoid.
It won't exit on Ctrl+C
It exits within about 10 seconds. Shutdown stops accepting new connections and waits for in-flight requests to finish. If a request is hung against a slow upstream, the grace period has to expire before the process exits.
Request problems
401 on every request
keys: is non-empty, so you're in multi-tenant mode and a valid
Authorization: Bearer <key> is required. Check the key matches an entry in
keys: exactly — a trailing newline from a copy-paste is the usual culprit.
429 immediately, even on a fresh budget
The worst-case estimate for this one request exceeds the user's entire daily_limit_usd.
Remember the estimate uses max_tokens, or a 4096-token ceiling when you don't set one, so a
request with no max_tokens against an expensive model can exceed a small budget on its own.
Fix by raising the budget, setting a realistic max_tokens, or both. See how estimation works.
A budget seems to have no effect at all
Almost always a keys: entry whose user_id has no matching
users: entry — a typo, or the user block was removed later. "No budget entry for this user"
is treated as "no limit," so this fails open.
fitguard run warns about this at startup. fitguard init can't
produce it, since it always writes both together, but a hand-edited config can.
A model 404s or hits the wrong provider
Routing is prefix-based on the model name. If your model doesn't match a known prefix, it defaults to
openai. Force it explicitly with a provider prefix:
"model": "groq/my-custom-model"
See the full routing table.
Responses look truncated
Check for the X-AI-Guard-Warning header. Its presence means
finish_reason: "length", so max_tokens cut the response off.
This normally goes unnoticed because a truncated response is still returned as a success.
On streamed responses that header can't be sent (headers go out before the body). Check the dashboard or logs instead.
Dashboard problems
I can't log in, or I forgot the password
fitguard reset-dashboard-password
No email, no server restart, and you don't need the old password. It also doubles as first-time setup if you skipped the
login during init. It rotates the session secret, so existing sessions are signed out too.
Login fails in production but worked locally
The most common cause: the deployed instance is reading a different config.yaml than the
one you set the password in. The dashboard account lives in that file, so if the container has a different
config mounted (or a fresh one generated at build time), your credentials aren't there.
Confirm the running instance is using the file you think it is with --config, and that it contains a dashboard: block with both session_secret and a user entry.
The dashboard is empty after every deploy
Ephemeral filesystem. Your fitguard.db is being wiped. See persistence and operations.
The dashboard shows only some of my traffic
You're running more than one instance. Cost logging is per-instance because fitguard.db is a
local file, so each dashboard shows only what that instance handled. This is true even with
budget.backend: redis. See scaling and operations.
The live feed stops updating behind a proxy
The dashboard updates over Server-Sent Events, and nginx buffers those by default. Set proxy_buffering off; — see the VPS HTTPS setup.
database is locked
Multiple processes are pointed at the same fitguard.db. Give each instance its own data_dir, or use genuinely concurrent-safe shared storage.
Known limitations
Current gaps, so you can check them against your requirements before adopting it.
A dropped stream ends without a formal error
Fallback protects you only before the first byte reaches the client. Once a provider's stream is committed, there's no way to retroactively signal failure if it dies partway through. Whatever was streamed is still logged and costed for what it actually delivered.
Embeddings don't use the fallback list
One attempt against the requested model's provider. Chat fallback models aren't valid embedding models, and swapping models partway through an indexing run would write mismatched dimensions into your vector store, so an error is returned instead.
Budget state is per-process unless you use Redis
And cost logging is per-process regardless. See scaling and operations.
Cost estimation is a heuristic
Not exact token counting — it's a pre-flight ceiling used to reserve budget before the upstream call, not the actual token count. It can reserve more than a request ends up costing; that's intentional, since the real completion size isn't known until the response comes back. Text is sized at ~4 chars/token. Images contribute a flat, deliberately generous per-image estimate (pixel dimensions aren't decoded), so a multimodal request no longer reserves as if its images were free.
The pricing table drifts
It's a manually maintained snapshot of public pricing, so it can go stale when a provider changes prices. Correct
a stale entry, or add a model the table doesn't know about yet, with a pricing: block
in config.yaml — no fitguard release needed. See
configuration. Embedding models are still priced input-only (there's no
completion side to an embedding request).
The dashboard has one account, not roles
No read-only or viewer accounts yet. Anyone with that one password sees and does everything the dashboard exposes.
No Assistants or Responses API
/v1/chat/completions and /v1/embeddings only.
Still stuck
Open an issue on GitHub.
Include your fitguard run startup output (it lists providers, cache, and budget config, and
redacts credentials), the status code and type from the error body, and your config with keys
removed.