Troubleshooting
The short version: the sidebar status dot tells you whether something is
wrong, docker compose logs tells you what. This page collects the
failures self-hosters actually hit, most common first.
First checks
Section titled “First checks”docker compose ps # both services up?curl -s http://localhost:8000/api/v1/health | jq .docker compose logs --tail=50 finance # the API + dashboard containerdocker compose logs --tail=50 imap-poller # the email poller sidecarThe health endpoint is unauthenticated and returns the same state as the sidebar dot:
| Status | Meaning |
|---|---|
ok (green) | Poller checked in within 5 minutes (or IMAP isn’t configured), nothing quarantined. |
degraded (amber) | Last poll 5–30 minutes ago, or an email failed to parse in the last 7 days — the early signal that a bank changed its template. |
stale (red) | No poll in over 30 minutes, or no new transaction in 14 days. |
degraded and stale describe the email pipeline, not the API — the
dashboard keeps working either way.
Transactions stopped appearing
Section titled “Transactions stopped appearing”The most common failure in normal operation, and it’s usually not your setup: a bank changed its email template.
- The dot goes amber; unparsed alerts collect under Needs review rather than being dropped.
docker compose logs imap-pollershowsFailed to parse email.- Open a parser-broken issue with a redacted email body — or fix it yourself with the add-a-parser guide, which is exactly the tutorial for this.
Meanwhile, entries recovered by AI extraction (if enabled) and manual entries from Needs review keep the journal complete.
The IMAP poller can’t connect
Section titled “The IMAP poller can’t connect”Gmail sign-in failures, [AUTHENTICATIONFAILED], repeated reconnects:
- Gmail needs an App Password, not your account password, and 2-Step Verification has to be on first.
- The poller backs off automatically (5s doubling to 5 minutes) and recovers
on its own once credentials are right — no restart needed after fixing
.env, butdocker compose restart imap-pollerapplies it immediately. - The email setup guide has the per-symptom detail, including IMAP being disabled on the account.
The dashboard won’t load
Section titled “The dashboard won’t load”- Port 8000 already in use — change the host side of the mapping in
docker-compose.yml(for example"8001:8000"), or use the port snippet indocker-compose.override.yml.example. Then openhttp://localhost:8001. - Container restarting or exited —
docker compose ps, thendocker compose logs financeand read the last screen; the failing line is almost always in it. exec format erroron Raspberry Pi / Apple Silicon — images are multi-arch (amd64 + arm64), so this is usually a stale cache:docker compose pull, or build locally.
Permission errors on data/
Section titled “Permission errors on data/”The named volume is owned by the container user. This appears after
switching between a bind mount and the named volume. The reset is
docker compose down -v — this deletes the volume and your data, so
take a backup first if there’s anything real in it.
Forgotten dashboard password
Section titled “Forgotten dashboard password”Stop the app, remove the app_password_hash key from data/config.json,
start it again — the dashboard returns to trust-on-first-use and prompts for
a new password. Under the Docker stack the file lives in the volume:
docker compose exec finance python -c \ "from src.finance.app_config import update_config; update_config({'app_password_hash': None})"docker compose restart financeNotifications aren’t arriving
Section titled “Notifications aren’t arriving”- With no provider configured, notifications go to the logs only — look for
NOTIFY [Transaction]:lines indocker compose logs finance. That’s the fallback, not a bug. - More than one provider’s variables set? Auto-detection has a precedence
order; pin it with
NOTIFICATION_PROVIDER. The notifications guide walks each provider’s failure modes.
Demo mode won’t go away
Section titled “Demo mode won’t go away”Demo mode is a config key, not an env var: set demo_mode: false in
data/config.json (or from Settings), then restart. Your real data lives in
data/finance.db; the seeded demo stays in data/demo.db and never mixes.
Still stuck
Section titled “Still stuck”- Health state, last poll, and version in one line:
curl -s http://localhost:8000/api/v1/health | jq . - “How do I…” questions go to GitHub Discussions; reproducible bugs go to Issues — include the health JSON and the last ~20 log lines, redacted.