Error reference
Every failure throws a typed class with .status (HTTP code) and a machine-readable .code. Branch on .code, not on message strings — messages may change, codes won't.
All four classes extend FlarelinkError (which extends Error), so err instanceof FlarelinkError catches any of them. .code is string | undefined — a few low-level transport failures arrive without one.
AuthError
Thrown by flarelink.auth.*. The .code is BetterAuth's machine-readable code, surfaced verbatim — so additional codes beyond the common ones below can appear. Handle the ones your UI cares about and re-throw the rest. getMe() is the one exception: it swallows a 401 and returns null instead of throwing.
| Code | Status | Cause & fix |
|---|---|---|
INVALID_PASSWORD | 401 | Wrong password on sign-in. Show a generic "email or password is incorrect" (don't reveal which). |
USER_NOT_FOUND | 401 / 404 | No account for that email. Same generic message as above to avoid account enumeration. |
USER_ALREADY_EXISTS | 422 | Sign-up with an email that's taken. Offer sign-in or password reset instead. |
EMAIL_NOT_VERIFIED | 403 | Sign-in blocked because verification is required and the email isn't verified yet. Prompt the user to check their inbox — the Worker auto-resends the link on sign-in attempts. |
TOO_MANY_REQUESTS | 429 | Rate limit (per-IP) tripped. Back off and retry; surface a "try again in a moment" message. |
MISSING_OR_NULL_ORIGIN | 403 | The request's Origin isn't in the deployment's trusted origins (or is missing). Add your app's origin in the Auth panel. The most common setup error — see Trusted origins. |
StorageError
Thrown by flarelink.storage.*. The codes are emitted by your auth Worker's storage routes (and one is client-side).
| Code | Status | Cause & fix |
|---|---|---|
MISSING_SERVICE_KEY | 400 | Client-side: you called flarelink.storage without passing serviceKey to createFlarelink. (Thrown as MissingServiceKeyError.) Construct a server-side client with the key. |
INVALID_SERVICE_KEY | 401 | The key doesn't match the deployment's stored hash. Rotated it recently? Update your env. Mistyped? Re-copy from the dashboard. |
SERVICE_KEY_NOT_PROVISIONED | 412 | No service key has been minted for this (legacy) deployment yet. Hit Redeploy on the Auth panel — it mints one and reveals it once. |
R2_NOT_CONFIGURED | 412 | R2 credentials aren't set for the project. Attach R2 / generate or paste a keypair on the Files page. See Rotating R2 credentials. |
DatabaseError
Thrown by flarelink.from(…) and flarelink.sql\`…\`. The first group is caught client-side before the request is sent (programmer error); the second is propagated from your auth Worker's D1 routes.
Client-side (validation, status 400)
| Code | Cause & fix |
|---|---|
INVALID_IDENTIFIER | A table or column name that isn't /^[A-Za-z_][A-Za-z0-9_]*$/. Don't interpolate user input as an identifier. |
UNSUPPORTED_FILTER | A .where() value that isn't equality (e.g. an array or object). Use flarelink.sql\`…\` for IN, ranges, OR. |
INVALID_LIMIT / INVALID_OFFSET | A non-integer or negative .limit() / .offset(). Pass a non-negative integer. |
EMPTY_INSERT / EMPTY_ROW | .insert() with no rows, or a row with no columns. Provide at least one column. |
EMPTY_PATCH | .update({}) with no columns to set. Provide at least one. |
Server-side (propagated from D1)
| Code | Status | Cause & fix |
|---|---|---|
INVALID_SQL | 400 | Empty or malformed SQL reached the Worker. Check your flarelink.sql\`…\` template. |
D1_QUERY_FAILED | 400 / 500 | D1 rejected the query (syntax, missing table/column, constraint violation). The underlying D1 message is included — read it. |
INVALID_BATCH | 400 | A batch request was malformed (the internal /api/db/batch route). Generally not hit through the public SDK. |
D1_BATCH_FAILED | 400 / 500 | A batch hit a D1 error; the whole batch rolled back atomically. |
INVALID_SERVICE_KEY / SERVICE_KEY_NOT_PROVISIONED | 401 / 412 | Same as in StorageError — DB routes require the service key too. |
MissingServiceKeyError
A dedicated subclass thrown immediately (code MISSING_SERVICE_KEY, status 400) when you call flarelink.storage or flarelink.from(…) / flarelink.sql\`…\` without passing serviceKey to createFlarelink. It's a fail-fast guard against accidentally shipping a server-only call to the browser. Construct a separate server-side client with the key — never expose the key client-side.