Error codes

Every status code walkindb can return, when you'll see it, and what to do about it.

All error responses use the same JSON shape:

{"error": "short human-readable message"}

400 Bad Request

invalid json

The request body is not valid JSON. Most commonly caused by embedded newlines inside a JSON string literal — JSON does not allow literal newlines inside "..." strings, only escaped \n.

# WRONG — raw newline inside the JSON string
-d '{"sql":"CREATE TABLE t(x);
         INSERT INTO t VALUES(1)"}'

# RIGHT — single line, or escape as \n
-d '{"sql":"CREATE TABLE t(x); INSERT INTO t VALUES(1)"}'

missing sql

The JSON body parsed but did not contain a non-empty sql field.

forbidden sql keyword: <keyword>

walkindb's application-level blocklist rejected the query before it reached SQLite. The message names the matched keyword.

Forbidden keywords:

  • ATTACH / DETACH — walkindb is one-file-per-instance by design
  • load_extension — no dynamic loading, ever
  • readfile, writefile, edit — filesystem scalar functions
  • fts5_* — internal fts5 admin functions (CREATE VIRTUAL TABLE ... USING fts5(...) still works)
  • sqlite_dbpage, zipfile, unzip

The match is case-insensitive and the blocklist strips SQL comments before matching, so /* skip */ ATTACH still trips.

invalid sql: ...

SQLite returned a syntax or semantic error. The message includes SQLite's original text. Common variants:

  • invalid sql: near "...": syntax error
  • invalid sql: no such table: ...
  • invalid sql: no such column: ...
  • invalid sql: no such function: ...
  • invalid sql: string or blob too big — you hit the LENGTH=1 MB per-value cap
  • invalid sql: expression tree is too large — you hit the EXPR_DEPTH=50 cap
  • invalid sql: too many terms in compound SELECT — you hit the COMPOUND_SELECT=10 cap
  • invalid sql: too many attached databases — you tried to ATTACH (belt-and-suspenders check after LIMIT_ATTACHED=0)
  • invalid sql: like or glob pattern too complex — you hit the LIKE_PATTERN_LENGTH=100 cap
  • invalid sql: too many levels of trigger recursion

404 Not Found

instance not found

Returned when:

  • The X-Walkin-Session header contains a garbage / tampered token
  • The token is valid but the instance TTL has already expired (and the sweeper deleted the file)
  • The token is valid but signed with a rotated-out HMAC secret (walkindb rotates daily and keeps one previous secret, so tokens survive exactly one rotation cycle)

Walkindb returns the same 404 for all three cases so attackers can't distinguish "wrong token" from "expired" from "no such instance". If you see 404 and you know the token was recent, drop it and let the next call provision a new walk-in.

408 Request Timeout

query exceeded 2s timeout

The query ran for more than 2 seconds wall-clock and was interrupted. Common causes:

  • Recursive CTE with no termination condition
  • Missing index on a large JOIN or WHERE clause
  • Pathological LIKE or GLOB pattern

Interrupted queries don't partially commit — SQLite rolls back.

413 Payload Too Large

sql payload exceeds 8 KB

The request body is larger than 8192 bytes. Split your statements into multiple calls against the same session.

429 Too Many Requests

rate limit exceeded

Your source IP has exceeded 60 requests per minute. Back off and retry after 10 seconds.

new-instance rate limit exceeded

Your source IP has exceeded 10 new walk-in instance creations per minute. This is a separate bucket from the request rate limit. Reuse the session token you already have instead of provisioning fresh instances in a loop.

walkindb does not currently emit Retry-After headers. Back off for 10 s and try again; the refill rate for both buckets is smooth.

507 Insufficient Storage

instance storage quota exceeded

The walk-in database has hit the 10 MB max_page_count cap. SQLite itself refuses further writes. There is no way to grow the instance — provision a new one or use a different database entirely.

500 Internal Server Error

internal error

walkindb encountered an unexpected error not covered by any of the cases above. The detail is logged server-side but deliberately not echoed back to avoid leaking internals. If you can reproduce one, please email [email protected] with the exact request — we pay a bounty for security-relevant reproductions.

Handling errors in client code

Python

from walkindb import Client, WalkinDBError

db = Client()
try:
    db.execute("SELECT * FROM missing_table")
except WalkinDBError as e:
    if e.status == 400:
        print("bad sql:", e.error)
    elif e.status == 404:
        db.reset_session()  # start a new walk-in
    elif e.status == 429:
        # wait a bit and retry
        time.sleep(e.retry_after or 10)
    elif e.status == 507:
        # quota full — provision a new walk-in or give up
        db.reset_session()
    else:
        raise

JavaScript / TypeScript

import { Client, WalkinDBError } from "walkindb";

const db = new Client();
try {
  await db.execute("SELECT * FROM missing_table");
} catch (e) {
  if (e instanceof WalkinDBError) {
    if (e.status === 400) console.error("bad sql:", e.error);
    else if (e.status === 404) db.resetSession();
    else if (e.status === 429) await new Promise(r => setTimeout(r, (e.retryAfter ?? 10) * 1000));
    else if (e.status === 507) db.resetSession();
    else throw e;
  }
}

Also see