Files
TREK/wiki/Troubleshooting.md
Julien G. 499097fa3c align dev (#899)
* chore: bump version to 3.0.0 [skip ci]

* fix: resolve dead wiki links across install and config pages

* fix(reservations): restore correct day assignment for non-transport bookings

v3.0.0 switched the planner from rendering reservations by
reservation_time to rendering them by day_id (commit 3f61e1c), but
migration 110 only backfilled day_id for transport types. Tours,
restaurants, events and 'other' bookings kept whatever day_id was
stored in the DB — often the trip's first day, from older code paths
that defaulted it there — so after the upgrade those rows all show
up on day 1 regardless of their actual reservation_time.

- Migration 122: for every non-hotel reservation, null out any
  day_id / end_day_id that does not match the reservation's time,
  then backfill it from reservation_time / reservation_end_time.
  Idempotent; leaves already-correct rows alone.
- reservationService.createReservation / updateReservation now
  derive day_id / end_day_id from reservation_time /
  reservation_end_time when the client didn't send one explicitly,
  so the mismatch cannot reappear on new or edited bookings.
  Hotels are skipped because they store their date range on the
  linked day_accommodation.

* chore: bump version to 3.0.1 [skip ci]

* fix(oidc): normalize discovery doc issuer before comparison

Trailing slash in doc.issuer (e.g. Authentik) caused a mismatch against
the already-normalized configured issuer, breaking OIDC login entirely.

Closes #834

* test(systemNotices): exclude v3 upgrade notices from login_count-only tests

Tests that expect an empty notice list were using first_seen_version='0.0.0'
(DB default), which matches the existingUserBeforeVersion('3.0.0') condition
now that the app is at 3.0.1. Set first_seen_version='3.0.0' so only the
firstLogin condition controls visibility in these tests.

* chore: bump version to 3.0.2 [skip ci]

* fix(oidc): normalize id_token iss claim before issuer comparison (#837)

jwt.verify does an exact string match on the issuer. Providers like
Authentik include a trailing slash in the id_token iss claim while the
configured issuer is already normalized (no trailing slash), causing
every login attempt to fail with jwt issuer invalid.

Move the issuer check out of jwt.verify options and apply the same
trailing-slash normalization used in the discovery doc validation.
Also adds OIDC-SVC-033–036 unit tests covering exact match, trailing
slash, wrong issuer, and wrong audience cases.

Closes #834

* chore: bump version to 3.0.3 [skip ci]

* fix(oidc,ui): restore Authentik login and fix mobile delete dialog (#845)

OIDC: when OIDC_DISCOVERY_URL is explicitly set, trust the discovery
doc's issuer for id_token comparison instead of rejecting a path
mismatch as an error. Authentik (and similar realm-path providers)
return a canonical issuer like /application/o/<slug>/ that differs
from the operator's base OIDC_ISSUER. Strict equality blocked login
in 3.x despite working in v2. Default discovery (no custom URL) keeps
the strict check. Adds OIDC-SVC-037/038/039.

UI: ConfirmDialog and CopyTripDialog lacked the --bottom-nav-h
paddingBottom offset that other overlays already use. On mobile portrait
the action buttons were hidden behind the sticky bottom nav bar.

Closes #843
Closes #844

* chore: bump version to 3.0.4 [skip ci]

* fix(files): open attachments only in new tab (#840)

window.open with noreferrer returns null, which triggered the popup-blocked download fallback in addition to the new-tab open. Use a target=_blank anchor click instead.

* chore: bump version to 3.0.5 [skip ci]

* fix(journey,pdf): journey reorder sort_order + PDF multi-day transport (#848)

* fix(journey): make sort_order authoritative for within-day entry ordering

Reorder buttons appeared broken because the server ORDER BY put entry_time
before sort_order, so entries synced from trip places with differing times
would always sort by time regardless of sort_order writes. The client store
mirrored the same comparator, making even the optimistic update invisible.

- Change ORDER BY to (entry_date, sort_order, id) in getJourneyFull and listEntries
- Fix syncTripPlaces and onPlaceCreated to assign MAX+1 sort_order per day instead of day_number/0
- Update client store comparator to match
- Add DB migration to backfill sort_order using old effective key (entry_time, id) so existing journeys retain their visual order
- Add tests: JOURNEY-SVC-089–093, FE-STORE-JOURNEY-018–019

Closes #846

* fix(pdf): include multi-day transport return/arrival in PDF itinerary (#847)

Reservations were matched to days by pickup date only, so the end-day
card (e.g. car Return, flight Arrival) was silently dropped from the PDF.
Add span-aware helpers mirroring DayPlanSidebar logic: match by day_id/end_day_id
span, show reservation_end_time on end days, prefix title with phase label
(Return/Arrival/etc.), and use per-day position for sort order.

* test(pdf): add missing day_id to transport reservation fixture

* chore: bump version to 3.0.6 [skip ci]

* [Snyk] Security upgrade uuid from 9.0.1 to 14.0.0 (#849)

* fix: server/package.json & server/package-lock.json to reduce vulnerabilities

The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-UUID-16133035

* fix: bump fast-xml-parser version

---------

Co-authored-by: snyk-bot <snyk-bot@snyk.io>
Co-authored-by: jubnl <jgunther021@gmail.com>

* chore: bump version to 3.0.7 [skip ci]

* fix: hot fixes 23-04-2026 (#856)

* fix(packing): resolve avatar URL path in bag and category assignees (#854)

packingService was returning raw avatar filenames from the DB instead of
the full /uploads/avatars/<filename> path, causing broken profile images
for users with uploaded avatars.

* fix(budget): use Map.get() to fix category rename no-op (#855)

* fix(security): relax Referrer-Policy and document HSTS_INCLUDE_SUBDOMAINS (#862) (#863)

- Change Helmet default from no-referrer to strict-origin-when-cross-origin
  so browsers send the origin on cross-origin requests, allowing Google Maps
  API key restrictions by HTTP referrer to work correctly
- Document HSTS_INCLUDE_SUBDOMAINS in all deployment artifacts:
  .env.example, docker-compose.yml, README.md, unraid-template.xml,
  charts/values.yaml, charts/configmap.yaml, wiki/Environment-Variables.md

* fix(planner): prefetch budget items on trip page mount (#864)

Loads budgetItems alongside reservations when TripPlannerPage mounts so
the Budget category dropdown in ReservationModal and TransportModal shows
pre-existing categories on first open, regardless of whether the Budget
tab has been visited.

Closes #861

* fix(reservations): prevent Invalid Date when end time is set without end date (#866)

When reservation_end_time held a bare time string ("HH:MM"), fmtDate()
produced Invalid Date on the reservation card.

- Modal: when end date is blank but end time is filled, construct a
  same-day ISO datetime using the start date (prevents time-only strings
  from ever being persisted)
- Panel: derive endDatePart via regex so date-only end values ("YYYY-MM-DD")
  still show the multi-day range, while bare time strings are skipped and
  handled correctly by the existing time column logic

Closes #860

* fix(planner): format reservation end time instead of rendering raw ISO string (#867)

Closes #859

* fix(planner): wire Route toggle into mobile day sidebar (#850) (#868)

The per-booking Route icon was missing on mobile because the mobile
DayPlanSidebar invocation in TripPlannerPage didn't pass
visibleConnectionIds or onToggleConnection. Mobile PWA users couldn't
activate reservation map overlays without forcing desktop mode.

Also corrects the Map-Features wiki: fixes the setting name
("Booking route labels" not "Show connection labels"), documents the
route_calculation requirement for travel-time pills, and explains that
overlays are off by default and must be toggled per reservation.

* chore: bump version to 3.0.8 [skip ci]

* docs(wiki): add MCP OAuth troubleshooting entry for missing APP_URL

* Fix demo banner overlapping bottom tab bar on mobile

The demo welcome modal extended below the mobile bottom tab bar,
hiding the dismiss button so visitors couldn't close it.

- Use dvh so mobile URL bar is accounted for correctly
- Reserve ~80px of bottom padding for the tab bar
- Make the footer sticky so the dismiss button stays visible
  while scrolling through the modal content
- Bump z-index to ensure the overlay sits above the tab bar

* Fix 500 on reservation edit after DB reinit (issue #883)

saveEndpoints was bound at module load via db.transaction(...). When the
demo-mode hourly reset (or a self-hoster's backup restore) closes the DB
connection and reinitialises it, the bound transaction still references
the now-closed connection — every subsequent reservation save with an
endpoints field throws "The database connection is not open", which the
client surfaces as "Internal server error".

Bind the transaction lazily on each call so it always runs against the
current connection.

* Fix exit code 132 on old CPUs by replacing sharp with jimp (issue #888) (#895)

sharp's prebuilt Linux x64 binary requires SSE4.2 (x86-64-v2), causing a
SIGILL crash on older hardware (e.g. AMD A6-3420M). Replace with jimp, a
pure-JS image library with no native binaries. Also skip thumbnail generation
entirely when the Journey addon is disabled (the default), preventing the
issue for most installs regardless of the image library used.

* chore: Add Trademark policy

* chore: Add Trademark policy

* chore: bump version to 3.0.9 [skip ci]

---------

Co-authored-by: Maurice <61554723+mauriceboe@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Maurice <mauriceboe@icloud.com>
Co-authored-by: Xre0uS <36565320+Xre0uS@users.noreply.github.com>
Co-authored-by: snyk-bot <snyk-bot@snyk.io>
2026-04-26 23:18:22 +02:00

12 KiB

Troubleshooting

"Access token required" when changing password on first login

Cause: The session cookie has the Secure flag set, which means the browser will only send it over HTTPS. When accessing TREK over plain HTTP (e.g. http://192.168.1.x:3000), the browser silently drops the cookie and the server sees no session — returning "Access token required".

Fix: Choose one of the following options:

Option 1 — Use HTTPS. Access TREK via HTTPS with a valid SSL certificate.

Option 2 — Disable the Secure flag. Set COOKIE_SECURE=false in your Docker environment to allow the session cookie to be sent over plain HTTP:

environment:
  - COOKIE_SECURE=false

Note: Option 2 is only recommended for internal/home-lab deployments that do not use HTTPS. Do not use it on a publicly accessible instance. See Environment Variables.


WebSocket not connecting / real-time sync broken

Cause: Your reverse proxy is not forwarding WebSocket upgrade headers on the /ws path.

Fix: Add the following to your proxy config for the /ws location:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Without these headers, the WebSocket handshake fails and real-time sync will not work. See Reverse Proxy for a complete nginx and Caddy configuration. Caddy handles WebSocket upgrades automatically.


HTTPS redirect loop

Cause: FORCE_HTTPS=true is set but your reverse proxy is not forwarding the X-Forwarded-Proto: https header, so every request looks like plain HTTP and gets redirected indefinitely.

Fix: Ensure your proxy passes the X-Forwarded-Proto header to TREK. Also set TRUST_PROXY=1 so that Express uses the forwarded IP for rate limiting and audit logs:

environment:
  - FORCE_HTTPS=true
  - TRUST_PROXY=1

Note: The /api/health endpoint is always exempt from the HTTPS redirect so that Docker health checks continue to work over plain HTTP.

If you are accessing TREK directly on http://<host>:3000 with no proxy, remove FORCE_HTTPS entirely. See Environment Variables.


Encrypted settings lost / API keys not working after migration

Cause: The ENCRYPTION_KEY was changed or lost. All API keys, SMTP passwords, OIDC client secrets, and MFA TOTP secrets are encrypted at rest using this key. Without the original key, decryption fails.

Fix: See Encryption Key Rotation for the migration script that re-encrypts data under a new key. If the original key is gone entirely, the encrypted values are unrecoverable and must be re-entered in the admin panel.

Note: If you upgraded from an older version without setting ENCRYPTION_KEY, the server uses the following resolution order on startup: (1) ENCRYPTION_KEY env var, (2) data/.encryption_key file, (3) one-time fallback to data/.jwt_secret for legacy upgrades — the value is immediately written to data/.encryption_key so JWT rotation cannot break decryption later, (4) auto-generated fresh key for brand-new installs. Check data/.encryption_key for the key currently in use.


Locked out of MFA / lost authenticator

Fix: If you still have access to your account, use one of the 10 backup codes generated during MFA setup to complete login. After signing in, go to Settings > Security to disable or reconfigure MFA.

If you no longer have access to backup codes and cannot log in, an admin must disable MFA for your account directly in the database, or use the reset-admin.js script to regain access to an admin account. There is no per-user MFA reset in the Admin Panel UI — the Admin Panel only controls the global "require MFA for all users" policy. See Admin: Users and Invites.


Demo user cannot edit or create

Cause: The instance is running with DEMO_MODE=true. All write operations are blocked for the demo account by design.

Fix: This is intentional behavior for public demo deployments. If you are self-hosting and want full access, remove the DEMO_MODE variable (or set it to false). See Demo Mode.


Backup restore fails with "file too large"

Cause: Your reverse proxy has a default body size limit (commonly 1 MB or 10 MB) that is smaller than the backup ZIP. Backup archives include the full uploads directory and can be large.

Fix: Raise the body size limit in your proxy config. TREK's own backup upload cap is 500 MB. For nginx:

client_max_body_size 500m;

Add this to the location / block (or the specific backup route). See Reverse Proxy and Backups.


"Cannot find module" on startup

Likely cause: A Docker volume mount is missing or the /app/data and /app/uploads directories are not writable by the container process. TREK automatically creates all required subdirectories on startup (data/logs, data/backups, data/tmp, uploads/files, uploads/covers, uploads/avatars, uploads/photos) — if this fails because the volume is read-only or owned by the wrong user, startup will abort.

Fix: Check your Docker volume configuration. Both ./data:/app/data and ./uploads:/app/uploads must be mounted and writable. Run docker inspect <container> --format '{{json .Mounts}}' to verify the mounts are present and point to valid host paths. If the host directories are owned by root, the container's chown step (which runs as root before dropping to node) should correct permissions automatically — but if your host filesystem is read-only or permissions are locked down, grant write access manually:

sudo chown -R 1000:1000 ./data ./uploads

Encryption key regenerated on restart — stored secrets stop working

Cause: On every startup, TREK resolves its encryption key in this order: (1) ENCRYPTION_KEY env var, (2) data/.encryption_key file, (3) legacy data/.jwt_secret fallback, (4) auto-generate a fresh key. If neither the env var nor the data/ volume is persisted — for example after recreating a container without a volume mount — a new random key is generated and all stored secrets (SMTP password, OIDC client secret, API keys, MFA TOTP seeds) become unrecoverable.

Fix: Ensure ./data:/app/data is mounted as a persistent volume so data/.encryption_key survives restarts. Alternatively, pin the key explicitly:

environment:
  - ENCRYPTION_KEY=<your-key>

See Encryption Key Rotation for how to retrieve or rotate the key.


OIDC login returns "APP_URL is not configured"

Cause: When OIDC is enabled, TREK needs to know its own public URL to build the redirect URI. It resolves this from (1) APP_URL env var, (2) the first entry in ALLOWED_ORIGINS, (3) http://localhost:<PORT> as a last resort. If none of these are set and the request is not coming from localhost, TREK returns a 500 error.

Fix: Set APP_URL to the public URL of your instance:

environment:
  - APP_URL=https://trek.example.com

OIDC login fails with issuer mismatch

Cause: TREK validates that the issuer field in the provider's discovery document exactly matches the configured OIDC_ISSUER. A trailing-slash difference (e.g. https://auth.example.com vs https://auth.example.com/) is enough to fail.

Fix: Check the exact issuer value your provider advertises and match it:

curl -s https://<your-oidc-issuer>/.well-known/openid-configuration | jq .issuer

Set OIDC_ISSUER to that exact string.


OIDC login fails when provider is on a private/internal network

Cause: TREK's SSRF guard blocks outbound requests to private IP ranges by default. If your OIDC provider (e.g. Keycloak, Authentik) is running on an internal address, the discovery document fetch will be blocked with: Requests to private/internal network addresses are not allowed.

Fix:

environment:
  - ALLOW_INTERNAL_NETWORK=true

Password reset emails are not delivered / SMTP is silent

Cause: SMTP failures are logged but do not surface as errors to the end user — the "reset email sent" message appears regardless. Common causes: wrong SMTP_HOST or SMTP_PORT, bad credentials, firewall blocking outbound on the SMTP port, or a self-signed certificate on the SMTP server.

Fix:

  1. Check server logs for Email send failed:
    docker logs <container> 2>&1 | grep "Email send failed"
    
  2. If the error mentions TLS or certificate, set SMTP_SKIP_TLS_VERIFY=true.
  3. Verify the port: 587 for STARTTLS, 465 for implicit TLS, 25 for plain SMTP.
  4. Test connectivity from the container:
    docker exec <container> nc -zv <SMTP_HOST> <SMTP_PORT>
    

Note: If no SMTP is configured at all, TREK prints the reset link directly to the server logs (===== PASSWORD RESET LINK =====). This is useful for initial setup or self-hosted installs without email.


CORS error — API requests blocked in the browser

Cause: If ALLOWED_ORIGINS is set, only those origins are permitted. Any request from a different origin is rejected with a CORS error visible in the browser console.

Fix: Add your origin to the comma-separated list:

environment:
  - ALLOWED_ORIGINS=https://trek.example.com,https://other.example.com

If ALLOWED_ORIGINS is not set, TREK allows all origins (development default). See Environment Variables.


WebSocket closes immediately after connecting (codes 4001 / 4403)

Cause: The /ws endpoint requires an ephemeral token generated by the client immediately before connecting. If the token is missing, expired, or the user's session state changed, the server closes the connection with a specific code:

Code Reason
4001 No token, expired/invalid token, or user not found — re-login required
4403 MFA is required globally but the user has not enabled it

Fix:

  • Code 4001: Log out and log back in. If it persists, check that your reverse proxy is not stripping the token query parameter from the WebSocket upgrade request.
  • Code 4403: The user must enable MFA in Settings > Security, or an admin can disable the global MFA requirement in Admin > Settings.

Cause: The browser Clipboard API (navigator.clipboard) is only available in a secure context. When accessing TREK over plain HTTP on a non-localhost address, the API is unavailable and clipboard operations silently fail or show an error.

Fix: The only supported options are:

  • Access TREK over HTTPS with a valid SSL certificate.
  • Access TREK directly from http://localhost:<port> — browsers treat localhost as a secure context for the Clipboard API (unlike the session cookie, which always requires HTTPS regardless of hostname).

MCP OAuth flow does not initiate / "Connect" redirects but authentication never starts

Cause: TREK builds the OAuth 2.1 redirect URI from APP_URL. If APP_URL is not set, the authorization URL is constructed from a localhost fallback that external clients (Claude.ai, Claude Desktop) cannot reach, so the OAuth handshake never completes.

Fix: Set APP_URL to the public URL of your instance:

environment:
  - APP_URL=https://trek.example.com

Restart the container after adding the variable. Once set, clicking Connect in the MCP client should redirect to your TREK instance and complete the OAuth flow normally.

Note: APP_URL is required for any MCP OAuth integration. Without it, the authorization endpoint resolves to http://localhost:<PORT>, which is unreachable from external MCP clients.


MCP integration: "Too many requests" or "Session limit reached"

Cause: Each user is limited to 300 MCP requests per minute and 20 concurrent sessions by default. Exceeding either limit returns a 429 response.

Fix: Increase the limits via environment variables:

environment:
  - MCP_RATE_LIMIT=600          # requests per minute per user (default: 300)
  - MCP_MAX_SESSION_PER_USER=50 # concurrent sessions per user (default: 20)