Skip to content

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.

Terminal window
docker compose ps # both services up?
curl -s http://localhost:8000/api/v1/health | jq .
docker compose logs --tail=50 finance # the API + dashboard container
docker compose logs --tail=50 imap-poller # the email poller sidecar

The health endpoint is unauthenticated and returns the same state as the sidebar dot:

StatusMeaning
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.

The most common failure in normal operation, and it’s usually not your setup: a bank changed its email template.

  1. The dot goes amber; unparsed alerts collect under Needs review rather than being dropped.
  2. docker compose logs imap-poller shows Failed to parse email.
  3. 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.

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, but docker compose restart imap-poller applies it immediately.
  • The email setup guide has the per-symptom detail, including IMAP being disabled on the account.
  • 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 in docker-compose.override.yml.example. Then open http://localhost:8001.
  • Container restarting or exiteddocker compose ps, then docker compose logs finance and read the last screen; the failing line is almost always in it.
  • exec format error on Raspberry Pi / Apple Silicon — images are multi-arch (amd64 + arm64), so this is usually a stale cache: docker compose pull, or build locally.

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 -vthis deletes the volume and your data, so take a backup first if there’s anything real in it.

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:

Terminal window
docker compose exec finance python -c \
"from src.finance.app_config import update_config; update_config({'app_password_hash': None})"
docker compose restart finance
  • With no provider configured, notifications go to the logs only — look for NOTIFY [Transaction]: lines in docker 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 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.

  • 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.