Skip to content

Troubleshooting

Section titled ““Narratorr isn’t connected yet” on search”

Search and requests need the narratorr connection. An admin must set narratorr’s Server URL (full base URL including scheme, e.g. http://narratorr:3000) and API key on the Settings page and Save (use Test first to verify). Until then, search returns this notice.

Container crash-loops with SQLITE_CANTOPEN

Section titled “Container crash-loops with SQLITE_CANTOPEN”

The log shows a libSQL open failure — Unable to open ... database file, SQLite code 14 (SQLITE_CANTOPEN) — and the container exits and restarts in a loop (migrations run before the server listens, so a database it can’t open is a hard boot failure). The container runs as the non-root node user (uid 1000) and can’t write its data directory.

This happens with a host bind mount whose directory is owned by root. Fix the owner on the host and restart:

Terminal window
sudo chown -R 1000:1000 /path/on/host/narratorr-requests
docker restart narratorr-requests

A named volume avoids this entirely — see Installation.

The app fails fast at boot (rather than come up insecure) if:

  • SESSION_SECRET is unset (production only) — set it (openssl rand -hex 32).
  • AUTH_BYPASS is enabled (production only) — it’s refused entirely there; set AUTH_BYPASS=0.
  • No auth method is enabled (any environment) — enable LOCAL_AUTH and/or configure an OIDC provider.
  • The environment is malformed — an incomplete OIDC_<ID>_* block, an invalid or duplicate provider id, an unreadable *_FILE secret, a bad BOOTSTRAP_ADMIN — the startup error names the variable.

Blank page (or can’t log in) over plain HTTP

Section titled “Blank page (or can’t log in) over plain HTTP”

The app renders a blank page, or the page loads but sign-in silently fails, on a production deploy with no TLS in front (no reverse proxy terminating HTTPS). This happens because BEHIND_TLS defaults to NODE_ENV=production’s value — so a plain-HTTP prod deploy inherits the CSP upgrade-insecure-requests directive (the browser tries to upgrade asset requests to HTTPS, which don’t exist, and blocks them) and a Secure session cookie (which a plain-HTTP browser won’t send back).

Fix: set BEHIND_TLS=false if you’re genuinely running production over plain HTTP with nothing terminating TLS in front. Leave it unset (or true) once a reverse proxy / load balancer does terminate TLS — see Installation.

Blank page (or can’t log in) over plain HTTP

Section titled “Blank page (or can’t log in) over plain HTTP”

The app renders a blank page, or the page loads but sign-in silently fails, on a production deploy with no TLS in front (no reverse proxy terminating HTTPS). This happens because BEHIND_TLS defaults to NODE_ENV=production’s value — so a plain-HTTP prod deploy inherits the CSP upgrade-insecure-requests directive (the browser tries to upgrade asset requests to HTTPS, which don’t exist, and blocks them) and a Secure session cookie (which a plain-HTTP browser won’t send back).

Fix: set BEHIND_TLS=false if you’re genuinely running production over plain HTTP with nothing terminating TLS in front. Leave it unset (or true) once a reverse proxy / load balancer does terminate TLS — see Installation.

AUTH_BYPASS won’t bind a non-loopback host

Section titled “AUTH_BYPASS won’t bind a non-loopback host”

By design — an AUTH_BYPASS instance has no auth, so it refuses to listen on anything but loopback. If you really mean to (an isolated host), set ALLOW_INSECURE_AUTH_BYPASS=1. Better: turn AUTH_BYPASS off and configure a real auth method.

A new user is stuck on “Awaiting approval”

Section titled “A new user is stuck on “Awaiting approval””

That’s the approval queue working as intended. An admin approves them on the Users page (→ active). The very first user becomes admin automatically; if no one is admin yet, see BOOTSTRAP_ADMIN in Authentication.

OIDC sign-in fails (“Sign-in failed. Please try again.”)

Section titled “OIDC sign-in fails (“Sign-in failed. Please try again.”)”

The provider callback didn’t complete. Check that:

  • OIDC_<ID>_REDIRECT_URI is set and byte-identical to the URI registered at the provider (https://<your-public-url>/api/auth/oidc/<id>/callback) — the app sends that configured value to the IdP and rebuilds the callback from it, never from the incoming Host header. A trailing slash or http: vs https: mismatch fails the exchange.
  • OIDC_<ID>_ISSUER, OIDC_<ID>_CLIENT_ID, and the client secret (if the provider requires one) are correct.
  • Your reverse proxy actually forwards /api/auth/oidc/<id>/callback through to the app.

Processing (acquiring) means narratorr is still working — searching, downloading, or importing. The timing is narratorr’s, not narratorr-requests’s: a request stays in Processing for as long as narratorr hasn’t imported the book yet (it may legitimately sit “wanted” until narratorr’s next scheduled search). Check the book’s state in narratorr. If it ultimately fails, the request flips to Failed with a reason.

eBook affordances appear only when all of these hold:

  • Enable ebook support is on in Settings → eBooks.
  • The connected narratorr also has eBook support enabled — narratorr-requests probes narratorr’s capability and hides everything eBook when it’s off (or the narratorr version predates it). The probe is cached, so give it a couple of minutes after flipping the narratorr side.
  • The book is In library (imported). A book still on the way shows no eBook action until narratorr finishes importing it.
  • narratorr actually has a companion EPUB paired with the book — an imported book without one shows a No eBook pill instead of the button.

Two different gates, told apart by the copy under the button:

  • “Send to Kindle isn’t available on this instance right now.” — instance-side: there’s no confirmed Kindle sender. An admin selects an Email (SMTP) notifier under Settings → eBooks. If it was working before, the selected notifier’s From address probably changed — that section shows an alert, and delivery stays paused until you Reconfirm sender. (The same copy also shows for a moment while the feature check is still loading — reopen the sheet before chasing config.)
  • “Send to Kindle needs your device address” — user-side: add your @kindle.com address in the account modal (click your username → Kindle address).

”Sent to Amazon” but nothing arrives on the Kindle

Section titled “”Sent to Amazon” but nothing arrives on the Kindle”

Delivery past that point is Amazon’s side of the handoff:

  • The approved-sender list is the usual culprit — Amazon silently drops personal documents from unapproved senders. Approve the app’s sender address (shown in the account modal and on the send sheet) on your Approved Personal Document E-mail Listnot your own Kindle address. One-time per Amazon account; steps in Setting up Send to Kindle.
  • Conversion and delivery take a few minutes — check the Kindle library or Amazon’s Content & Devices for the document before re-sending.
  • Books over Amazon’s 25 MiB personal-document cap are refused up front with a “download it instead” message — nothing is sent, so nothing will arrive.
  • Confirm the notifier exists and its Test passes on the Settings page.
  • Set the Public URL if you want deep-links back to the app in notifications (the queue or the Users page, depending on the event).
  • Admin events (New request, New signup, Request failed) fire only over notifiers that have that event checked — each notifier picks its own in its Events field. A notifier with no events selected even hides its Test button.
  • Requester emails (approved / denied / ready-to-listen) need three things: an Email (SMTP) notifier configured, a contact email on the requester’s account, and the matching opt-in checked in their account modal (click your username). Without an SMTP notifier the opt-ins render disabled with an explanation. Requesters can always track status on their My Requests page (which updates live).

GET /api/health reports readiness — it pings the database and reports whether narratorr is configured. The Docker image ships a matching HEALTHCHECK, so docker ps / your orchestrator will show the container as healthy/unhealthy.