ErrLookup › Background articles › Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first
Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first
Database query failed errors are 500s and wrapped exceptions that occur when a library's SQL query — Prisma, pg, database/sql, or an ORM — throws at runtime: the database is unreachable, locked, schema-drifted, or out of connections. Developers hit them as generic 'Internal Server Error' bodies, 'failed to build ... snapshot' responses, or wrapped messages like 'load wisp labels: %w', while the real database exception lives only in server logs. This page explains the shared mechanism, the most common causes, and the fixes that hold across libraries.
Distilled from 107 documented records across 16 repositories.
Background
This family forms wherever application code calls into a database layer and lets the failure propagate instead of handling it: a Prisma query over the workspaces table in AnythingLLM's API handlers, a pg pool query in worldmonitor's snapshot builders, a database/sql EXISTS probe in beads, a Prisma query_raw call in litellm, or a Django model query in label-studio. The pattern is the same at every layer: the query engine (Prisma's generated client, node-postgres Pool, Go's database/sql, Django ORM) raises a driver-level exception, the route's catch-all or a wrapper converts it to a generic response, and the specific cause — SQLITE_BUSY, missing table, connection refused, statement timeout — is only preserved server-side, in a console.error, fastify.log.error, verbose_proxy_logger traceback, or an appended {e} in the message text.
From the caller's side these errors are deliberately opaque. AnythingLLM returns a bare 'Internal Server Error' whose HTTP body carries no detail; worldmonitor's routes return fixed strings like 'failed to build overview snapshot' while fastify.log.error holds the real exception; litellm converts non-ManagementProblem exceptions into an opaque RFC 9457 problem with a stable urn and logs the traceback in the proxy. Some libraries are more transparent: beads wraps with %w so errors.Is/errors.As can unwrap driver.ErrBadConn or context.DeadlineExceeded, litellm's get_table_info chains __cause__, and siyuan appends the underlying SQL error text directly to 'get block failed: %s'. In every case the debugging move is the same — get to the underlying database error before touching client code.
Within the family, the trigger distribution varies by database backend. SQLite-backed projects (AnythingLLM, siyuan, claude-mem's local store) see lock contention — SQLITE_BUSY from concurrent writers, two server instances on one storage directory, backup jobs holding the file — plus corrupted or missing database files and schema drift when the app is upgraded without its boot-time migrations. Postgres-backed projects (worldmonitor, claude-mem's ingestion, litellm, nautilus_trader) see connection failures (DATABASE_URL unset, connect timeouts, pool exhaustion at max connections), statement timeouts, and failover resets. Dolt-backed beads adds schema and system-table concerns (dolt_remote_branches missing on old versions, migrations not applied).
A recurring design question in these records is fail-open versus fail-closed. Some paths deliberately degrade: worldmonitor serves cached snapshots where possible, claude-mem stores events unlinked when session lookup fails and repairs with a backfill, beads suppresses missing-table errors for wisp labels, and AnythingLLM's isOnboardingComplete returns false on inner failure. Others fail hard on purpose: beads' sweep contract requires aborting rather than under-scanning, so a reference-scan query error stops the prune entirely. Whether a given 500 is safe to retry depends on which side the library chose — read-only queries and keyset pages are generally idempotent, while state transitions and writes need idempotency checks before retry.
Common causes
- Database unreachable or connection failure. Postgres down, DATABASE_URL unset, connection dropped mid-query, or a pool exhausted at max connections. Worldmonitor's snapshot routes, litellm's usage queries, and beads' transaction probes all fail this way; the fix is verifying connectivity from the API process (e.g. a SELECT 1) before debugging code.
- Schema drift — migrations not applied. The app was upgraded but the database schema was not migrated, so queries reference missing tables or columns (P2021 'table does not exist', 'no such column', missing LiteLLM_* columns). AnythingLLM admin endpoints and litellm budget/usage endpoints both surface this; re-running migrations or restarting so boot migrations complete resolves it.
- Database locked or concurrent-writer contention. SQLite files held by a backup job, a second server instance on the same storage directory, or parallel writes causing SQLITE_BUSY; Postgres deadlocks or lock timeouts under concurrent transitions (label-studio). Serialize writers and keep one process per storage directory.
- Statement timeout or context cancellation. statement_timeout firing on large queries (litellm usage over big date ranges, nautilus_trader migrations), or a Go context deadline exceeded mid-transaction (beads). Bound result sets, raise timeouts for known-heavy operations, and retry only transient cases.
- Corrupted, missing, or unreadable database file. A SQLite file corrupted by a crash, truncated JSON rows in embed chat history, a storage volume mounted read-only or deleted mid-run, or STORAGE_DIR permission problems. Restore from backup, fix volume permissions, and re-run indexing where supported (siyuan content index rebuild).
- Proxy or infrastructure killing the session. Transaction-pooling proxies like PgBouncer dropping a migration session mid-transaction (nautilus_trader), failovers, or connection health issues producing ErrBadConn. Run migrations over a direct or session-pooled connection and use pools with health checks.
- Missing or invalid input data reaching the query. A market/basket/range combination with no computed rows hitting throwing expectations in worldmonitor, a NaN days parameter flowing into SQL, a malformed block id whose row is absent from the index (siyuan), or fabricated cursors breaking keyset paging (beads). Validate parameters against supported sets before the query layer.
What usually fixes it
- Read the server-side log, not the response body: in this family the HTTP body or wrapper message is intentionally generic, and the real database exception is printed by console.error, fastify.log.error, verbose_proxy_logger, or carried in the wrapped cause. Identify the Prisma/driver error code first.
- Verify database connectivity and health before changing code: probe with SELECT 1 or a health endpoint, confirm DATABASE_URL/STORAGE_DIR resolve from the API process, and check pool exhaustion, disk space, and failover state.
- Bring schema and deployed code back in lockstep: apply migrations after every upgrade, restart the server so boot-time migrations and Prisma schema sync complete, and restore the database from a backup taken on the same application version if drift or corruption persists.
- Eliminate concurrent access: one server process per SQLite storage directory, serialize or queue heavy writes, avoid running embeds/admin jobs against the same store in parallel, and use connection pools with health checks for Postgres.
- Retry only what is safe: read-only lists, keyset-paged comments, and idempotent migrations can be retried with backoff on transient errors; state transitions and writes need idempotency checks (re-read current state first), and context cancellation should never be retried blindly.
- Degrade gracefully where the library allows it: serve cached snapshots with staleness markers, treat embed-history 500s as 'start fresh', and schedule backfill jobs for paths the library stores degraded (like unlinked events) so the repair is automated, not manual.
Go deeper
- Connection failures: ECONNREFUSED, ECONNRESET, and friends — why connections get refused, reset, or dropped.
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Documented occurrences
- Internal Server Error (Mintplex-Labs/anything-llm)
- Internal Server Error (Mintplex-Labs/anything-llm)
- failed to build coverage snapshot (koala73/worldmonitor)
- Internal Server Error (Mintplex-Labs/anything-llm)
- failed to build basket series snapshot (koala73/worldmonitor)
- load wisp labels: %w (gastownhall/beads)
- failed to build movers snapshot (koala73/worldmonitor)
- failed to build overview snapshot (koala73/worldmonitor)
- failed to build retailer spread snapshot (koala73/worldmonitor)
- Internal Server Error (Mintplex-Labs/anything-llm)
- [embed] could not count persisted embeddings; leaving stats.embeddings untouched (abhigyanpatwari/GitNexus)
- Error retrieving usage data: {e} (BerriAI/litellm)
- check issue existence: %w (gastownhall/beads)
- failed to build categories snapshot (koala73/worldmonitor)
- Could not find an API Keys. (Mintplex-Labs/anything-llm)
- Failed to fetch API keys (Mintplex-Labs/anything-llm)
- urn:litellm:error:internal-server-error: Failed to list budgets. (BerriAI/litellm)
- get issue comments page from %s (after %v/%q): %w (gastownhall/beads)
- Error getting current state: {e} (HumanSignal/label-studio)
- list remote-tracking refs: %w (gastownhall/beads)
…and 87 more across the corpus — use search.
Honest provenance: generated on 2026-08-30 from AI-assisted analysis of the linked records. See how records are made.