Troubleshooting
Common issues and how to resolve them.
XRPC endpoint returns 404
Symptom: GET /xrpc/your.method.name returns {"error": "method not found"}.
Causes:
- The lexicon hasn't been uploaded yet. Check with
GET /admin/lexiconsor the dashboard. - The lexicon's
defs.main.typedoesn't match the HTTP method. Queries areGET, procedures arePOST. - The NSID in the URL doesn't match the
idfield in the uploaded lexicon JSON.
Queries return empty results
Symptom: The XRPC query endpoint returns {"records": []} even though records should exist.
Causes:
- The query lexicon is missing a
target_collection. Without it, the query doesn't know which records to read. See Lexicons - target_collection. - The record-type lexicon hasn't finished backfilling. Check backfill status with
GET /admin/backfill/statusor the dashboard. - Records exist on the network but HappyView hasn't indexed them yet. Jetstream only delivers events from after the collection was added to the filter. Use backfill to import historical records.
Procedure returns 401 Unauthorized
Symptom: POST /xrpc/your.method.name returns {"error": "..."} with status 401.
Causes:
- No
Authorization: DPoPheader orX-Client-Keyheader is present. - The DPoP proof is invalid or expired.
- The API client key is not registered or is inactive.
Admin endpoints return 403 Forbidden
Symptom: Admin API calls return {"error": "forbidden"}.
Causes:
- Your DID is not in the users table. Ask an existing user with
users:createpermission to add you viaPOST /admin/users. - If this is a fresh deployment with no users, the first authenticated request to any admin endpoint automatically bootstraps you as the super user. Make sure you're logged in via the dashboard or using a valid API key.
- You may be in the users table but lack the required permission for the endpoint you're calling. Check your permissions with
GET /admin/usersor ask a user withusers:updatepermission to grant the permission you need.
Permission denied errors
Symptom: Admin API calls return {"error": "insufficient permissions"} with status 403, even though you can access other endpoints.
Causes:
- Your user account doesn't have the specific permission required by the endpoint. Each endpoint requires a specific permission — see the permissions table.
- If using an API key, the key's effective permissions are the intersection of the key's permissions and your user permissions. A key can never have more access than the user who created it.
- Only the super user can call
POST /admin/users/transfer-super. This endpoint cannot be accessed with any permission — it requires super user status.
Lua script errors
Symptom: An XRPC endpoint returns {"error": "script execution failed"} or {"error": "script exceeded execution time limit"}.
What to do:
- Check the server logs: the full error message is logged at error level but not exposed to the client.
- Use
log("message")in your script to trace execution. Output appears in server logs at debug level (requiresRUST_LOGto include debug). - If you hit the execution limit, your script likely has an infinite loop or is processing too much data. See Lua Scripting - Sandbox.
See Lua Scripting - Debugging for more.
Backfill job stuck in "pending" or "running"
Symptom: A backfill job doesn't progress or stays in pending.
Causes:
- The backfill worker processes one job at a time. If another job is running, yours will wait.
- The relay (
RELAY_URL) may be unreachable or slow to respond. Check connectivity. - Individual PDS fetches can fail silently. The worker logs warnings and continues. Check server logs for details.
See Backfill for how the process works.
Records not appearing in real time
Symptom: New records created on the network don't show up in queries.
Causes:
- HappyView receives real-time events via Jetstream. Verify the
JETSTREAM_URLis reachable and check server logs forjetstream.disconnectedevents. - No record-type lexicon exists for the collection. HappyView only indexes collections that have a corresponding record-type lexicon.
- The Jetstream subscription hasn't reconnected with the new collection filter after a lexicon change. This should happen automatically. Check server logs for connection errors.
Lua script can't find records
Symptom: db.query or db.get returns empty results inside a Lua script, even though the admin dashboard shows records exist.
Causes:
- The
collectionglobal is only set when the lexicon has atarget_collection. If you're usingdb.rawwith a hardcoded collection name, double-check the spelling matches exactly. db.getexpects a full AT URI (at://did:plc:abc/collection/rkey), not just an rkey.- If querying by DID, make sure you're passing the full DID string including the
did:plc:ordid:web:prefix.
Plugin secrets not working
Symptom: A plugin fails with authentication errors even though you've configured its secrets.
Causes:
TOKEN_ENCRYPTION_KEYis not set. Plugin secrets are encrypted at rest and cannot be read without this key. See Plugins - Configuration.- If
TOKEN_ENCRYPTION_KEYchanged since the secrets were saved, the existing encrypted values are unreadable. Re-enter the secrets via the dashboard orPUT /admin/plugins/{id}/secrets. - Environment variable secrets (
PLUGIN_<ID>_<KEY>) are overridden by dashboard-configured secrets. If you've set both, the dashboard values take precedence.
OAuth or login issues
HappyView handles atproto OAuth internally via the atrium-oauth library. If users can't log in:
- Verify
PUBLIC_URLis set correctly and the URL is publicly accessible (required for OAuth callbacks). - Check that the user's PDS authorization server is reachable.
- Verify
SESSION_SECREThasn't changed since sessions were created (changing it invalidates all existing dashboard sessions). - Check server logs for OAuth-specific error messages.
Third-party app can't authenticate
Symptom: A third-party app using DPoP authentication gets 401 errors on XRPC endpoints.
Causes:
- The app hasn't registered an API client. Every XRPC request needs an
X-Client-Keyheader with a validhvc_-prefixed client key. Register one via Settings > API Clients orPOST /admin/api-clients. - The DPoP proof is malformed or expired. Proofs include a timestamp and are valid for a short window.
- The API client has been deactivated (
is_active: false). Re-enable it via the dashboard orPUT /admin/api-clients/{id}.
Database connection errors
Symptom: HappyView fails to start or returns 500 errors.
Causes:
DATABASE_URLis not set or points to an unreachable Postgres instance.- The database user doesn't have sufficient permissions. HappyView needs to create tables (migrations run automatically on startup).
- Postgres version is too old. HappyView requires Postgres 17+.
See Configuration for environment variable details.
Switching databases loses data
Symptom: After changing DATABASE_URL from SQLite to Postgres (or vice versa), all records, lexicons, and users are gone.
Explanation: Each database is independent. Switching DATABASE_URL points HappyView at a fresh database. Your old data is still in the previous database file or Postgres instance.
Recovery: Re-upload your lexicons and run backfills to re-index records from the network. Admin settings, users, and API keys need to be re-created manually. See the SQLite → Postgres or Postgres → SQLite migration guides.
Jetstream disconnects frequently
Symptom: Server logs show repeated jetstream.disconnected / jetstream.connected events.
Causes:
- Network instability between HappyView and the Jetstream server. Verify
JETSTREAM_URLis reachable. - The default Jetstream instance may be under heavy load. Consider pointing
JETSTREAM_URLat a different instance if available. - HappyView reconnects automatically and resumes from its last cursor, so brief disconnections don't cause data loss. Prolonged outages may require a backfill to catch up on missed records.