Troubleshooting
If something’s broken, find your symptom below. Most issues have a two-line fix. Anything that needs more than that points to a file you’ll already know how to read.
Container won’t start
Section titled “Container won’t start””Database connection timeout after 30 attempts”
Section titled “”Database connection timeout after 30 attempts””The app waits 60 seconds for Postgres before giving up. Most likely causes:
- Postgres container itself crashed —
docker compose logs dbwill show why. Most common: wrongDB_PASSWORD(it changed since the volume was created — Postgres won’t accept the new password against existing data). DATABASE_URLis wrong — pointing at a host that doesn’t exist on the Docker network. The bundled compose usesdbas the hostname.- DNS / network isolation — Docker bridge network not created, or the app container is on a different network than
db.
Try first: docker compose -f docker-compose.prod.yml ps — if db
isn’t (healthy), fix that before fixing the app.
”Migration failed with exit code 1”
Section titled “”Migration failed with exit code 1””The entrypoint will retry on the next boot, but you’ll see the underlying error in the logs. Common cases:
- Schema drift — someone edited the database manually and Prisma sees columns it didn’t add. Resolve by rolling back the marker row:
docker exec travstats-app npx prisma migrate resolve --rolled-back <migration_name>then restart. - Disk full —
df -hon the host. Postgres needs free space for transaction logs even on a read-only failed migration. - Missing PostGIS extension — only happens on external-Postgres setups. Run
CREATE EXTENSION postgis;against the database, then restart.
”JWT_SECRET is invalid”
Section titled “”JWT_SECRET is invalid””The persisted secret got truncated, partially overwritten, or its file lost permissions. Delete it and let the entrypoint regenerate (this signs out everyone — they re-log-in with the same password):
docker exec travstats-app rm /app/data/secrets/jwt.secretdocker compose -f docker-compose.prod.yml restart appSign-in problems
Section titled “Sign-in problems”Login loop / immediate logout
Section titled “Login loop / immediate logout”Almost always one of two things behind a reverse proxy:
-
X-Forwarded-Protois missing. TravStats reads it to decide whether to set theSecureflag on the JWT cookie. If your proxy doesn’t forward it and the browser is on HTTPS, the cookie is dropped and you’re back at login.nginx fix:
proxy_set_header X-Forwarded-Proto $scheme; -
COOKIE_SECURE=trueon plain HTTP. Inverse problem — you’re onhttp://travstats.lan:3010and the cookie is setSecure-only, so the browser drops it. Fix: leaveCOOKIE_SECUREunset (auto-detect) or set it explicitly tofalsefor LAN-only HTTP.
DevTools → Application → Cookies should show travstats_session set
on the response from /api/v1/auth/login. If it’s set there but
missing on subsequent requests, you’re hitting one of the above.
”Setup wizard keeps appearing”
Section titled “”Setup wizard keeps appearing””The frontend redirects to setup whenever /api/v1/setup/status
returns requiresSetup: true. That endpoint counts admin users — if
none exist, you get the wizard. Cause: someone deleted every admin.
docker exec -it travstats-db psql -U flights flights -c "SELECT id, username, is_admin FROM users WHERE is_admin = true;"If empty, promote a user manually or run the createAdmin.js
fallback from the first-run page.
”Forgot password” email never arrives
Section titled “”Forgot password” email never arrives”SMTP isn’t configured (or the configured server is rejecting). Admin → Settings → SMTP has a Send test mail button — start there. If the test fails, check your provider’s auth requirements (most need an app-specific password, not your account password).
If you’re locked out and can’t get to Admin → Settings: see the bcrypt-reset trick.
Parsing problems
Section titled “Parsing problems”Email parser returned no flight at all
Section titled “Email parser returned no flight at all”Most likely: the email is from an airline TravStats doesn’t have a template for, and Ollama isn’t reachable / not configured.
Check:
docker exec travstats-app curl -fsS http://ollama:11434/api/tagsShould list installed models. If the call 503s, the Ollama container isn’t running. If it lists no models, pull one:
docker exec travstats-ollama ollama pull gemma3:12bBuilt-in templates exist for: LH (Lufthansa, both old and new formats), LX (Swiss), OS (Austrian), SN (Brussels), FR (Ryanair), U2 (easyJet), EW (Eurowings), W6 (Wizz Air). For other carriers you can record a user template.
Email parser extracted wrong dates
Section titled “Email parser extracted wrong dates”The most-common case is multi-leg bookings where the parser picked up the wrong leg. The review screen lets you delete the unwanted suggestions before saving — nothing’s persisted until you click Save all.
If the date is consistently off by hours (not days), it’s a timezone issue — make sure your Admin → Settings → Timezone is set correctly. The container itself runs UTC; the UI converts to your configured display timezone.
Boarding pass scan returned blank fields
Section titled “Boarding pass scan returned blank fields”Vision parsers cascade in this order: Ollama vision model → OpenAI / Claude (if API key set) → Tesseract OCR → manual entry pre-fill.
If everything blanks out:
- Check
OLLAMA_URLpoints to a container with a vision-capable model installed (e.g.llama3.2-vision) - Try uploading a clearer photo — Tesseract is picky about glare and angle
- Check rate limits:
boardingPassParseLimiterallows N requests/min per user; busy users hit it during bulk re-imports
Map / globe doesn’t render
Section titled “Map / globe doesn’t render”Almost always WebGL or content-security-policy related.
- DevTools → Console. Look for errors mentioning
WebGL,WEBGL_lose_context, orCSP. - No errors but blank canvas? Try a hard reload (
Ctrl + Shift + R) — three.js can lose its WebGL context when the tab was backgrounded for a long time. - CSP blocking inline scripts? If you set a custom
Content-Security-Policyvia your reverse proxy, make surescript-srcincludes'self'andworker-srcallows blob: workers (deck.gl uses them). - Map tiles don’t load? Check the network tab — TravStats fetches from OpenStreetMap-compatible tile servers by default. If your firewall blocks them, point to a self-hosted tileserver under Admin → Settings → Map → Tile Server.
TravStats writes structured JSON via Pino to four files inside
/app/data/logs/:
| File | Contains | Default level |
|---|---|---|
app.log | Everything the application emits | info |
error.log | Errors and warnings only (subset of app.log) | error |
http.log | One line per HTTP request (method, path, status, latency, user) | info |
parser*.log | Parser pipeline traces (template detection, Ollama prompts, fallback decisions) | debug |
Read them
Section titled “Read them”From the host:
docker exec travstats-app tail -f /app/data/logs/app.log | jq .Or just docker compose logs -f app — Pino writes to stdout too,
so docker logs and the file see the same stream.
Raise verbosity for one debugging session
Section titled “Raise verbosity for one debugging session”In the UI: Admin → Settings → Logging → Level → debug, save.
Persists across restarts. Drop it back to info when done — debug
adds a lot of volume to app.log.
For one-off Prisma query inspection:
docker exec -e PRISMA_LOG_LEVEL=info travstats-app …Disk usage runaway
Section titled “Disk usage runaway”docker system dfdocker exec travstats-app du -sh /app/data/*Common culprits:
/app/data/logs/— unbounded growth if backups aren’t pruning.Admin → Settings → Logging → Retention./app/data/backups/— every nightlypg_dumpaccumulates. Lower the Admin → Settings → Backups → Retention setting.- Old image tags —
docker image prune -fafter upgrades.
When to file an issue
Section titled “When to file an issue”Before opening one, capture:
- TravStats version — bottom of any page in the UI, or
cat /app/backend/VERSIONinside the container. - The container logs around the failure —
docker compose logs --tail 200 app db. Redact any API keys. - A repro — what you clicked or which API call you made. Curl + headers if you can.
File at github.com/Abrechen2/TravStats/issues. The bug-report template will ask for exactly the things above.
The in-app Report Bug button (Settings → footer) builds an anonymised diagnostic bundle and copies it to your clipboard — paste it into the GitHub issue and you’ve covered most of what maintainers will ask.
Diagnostic export bundle
Section titled “Diagnostic export bundle”The bundle is a single JSON document — small enough to paste into an issue, structured so maintainers can grep through it. Same content as Admin → Settings → System info → Diagnostic export.
What’s in the bundle
Section titled “What’s in the bundle”- Recent log entries from
app.loganderror.log(latest ~500 lines per file) - System info — TravStats version, build date, Node version, Postgres version, database size, backup count, current health status
- Settings sketch — instance name, registration mode, configured-or-not flags for each external service (no values), log level, retention policy, backup schedule
- Log statistics — how many
error/warn/infolines in the rolling window
What’s redacted
Section titled “What’s redacted”The bundle goes through a defensive scrubber before download. Stripped entirely if they appear as object keys:
ip, ipAddress, userAgent, email, notificationEmail,
password, passwordHash, token, auth_token, authorization,
cookie, cookies, resetToken, changeToken, apiKey,
api_key, openaiApiKey, claudeApiKey, globalOpenaiApiKey,
globalClaudeApiKey, airlabsApiKey, aviationstackApiKey,
clientSecret, accessToken, refreshToken.
Pattern-replaced when they appear in any string value:
| Pattern | Replaced with |
|---|---|
JWT-shaped tokens (eyJ…) | <redacted:jwt> |
Email addresses ([email protected]) | <redacted:email> |
| IPv4 addresses | <redacted:ip> |
| UUIDs (user IDs, flight IDs, …) | <redacted:uuid> |
What’s NOT in the bundle
Section titled “What’s NOT in the bundle”- Flight data (no routes, no airlines, no dates)
- User account data (no usernames, no emails, no passwords)
- API key values for any external service
- Encryption key, JWT secret, or any other secret from
/app/data/secrets/ - Anything from the database tables — only logs + system info
When to use it
Section titled “When to use it”When you’re filing a GitHub issue. The bundle answers most of the “can you reproduce / what version / what config / what’s the error” questions in one paste. Without it, the maintainer has to ask follow-up questions for two days; with it, you usually get a fix on first reply.
If you’re nervous about residual PII in the redacted bundle, open the JSON in a text editor before pasting and grep for anything that looks identifying — the scrubber is conservative, but the “check before paste” step is yours to make.