Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Errors and limits

Error shape

Error bodies are plain text (Content-Type: text/plain; charset=utf-8), not JSON:

table "tasks" already exists
  • Every refused credential gets 403 with the body forbidden. It doesn’t say why, by design. relstore never answers 401.
  • 500 is always request failed; details stay out of responses.
  • 404 for an unknown path and 405 for a wrong method have empty bodies; a 405 carries an Allow header.
  • Other messages are written for people and may change. There are no error codes.
curl -sS https://relstore.stackwell.tech/v1/purge-requests | grep -Fx forbidden

Errors from request parsing

These come from reading the request, which relstore does only after it has accepted your token. Without one you get 403 whatever the body, path or query, so these never tell an unauthenticated caller what a route expects:

StatusCauseExample body
400The body is not valid JSON, or a path id is not a numberFailed to parse the request body as JSON: key must be a string at line 1 column 2
413Body larger than 1 MiBFailed to buffer the request body: length limit exceeded
415Body sent without Content-Type: application/jsonExpected request with `Content-Type: application/json`
422The JSON is missing a required field, has a field the route doesn’t take, or has an unknown type or opFailed to deserialize the JSON body into the target type: missing field `table` at line 1 column 2
curl -sS -o /dev/null -w '%{http_code}\n' -X POST https://relstore.stackwell.tech/v1/tables -d 'not json' | grep -Fx 403

Status codes

StatusMeaning
200Done. Every successful call returns 200, including creating a table.
400A table definition breaks a rule (no columns, a bad or reserved name, a column declared twice); a write sets a reserved column, leaves a column out, or names a key that is not a column; a patch names no column; a query names a column the table doesn’t have; a value has the wrong type for its column, in a write or a filter; begins_with or contains is used on a column that isn’t text; a rid is empty; a text value or rid contains a NUL character (\u0000); a query’s offset is over 9223372036854775807 or its filter holds over 65,534 values; or a table or column name is malformed.
403No token, a token relstore doesn’t accept, or a client whose role may not call this route.
404No such path, no such purge request, a table your database doesn’t have (table "nope" does not exist), or a patch or restore of a rid that was never written.
405Wrong method for this path.
408A statement ran longer than 5 seconds: statement exceeded the 5000ms timeout; narrow the query.
409The table already exists; an insert’s rid was already written; a patch of a soft-deleted row; a restore of a row that is not soft-deleted, or was never live; or, rarely, a write kept losing the race for its row’s next version to concurrent writes of the same row. That write changed nothing, so send it again.
422Besides parsing errors: a query matched more than 10,000 rows.
429Over the rate limit; see below.
500request failed: a fault on relstore’s side.
503A purge or erasure route (not enabled yet), or /health when the database can’t be reached.

A 400 or 404 names only your own table, column or value, for example value "high" is not assignable to column "priority" (bigint).

Rate limits

Each database may make 60 requests a minute, with bursts of up to 60. Its agent and operator clients share the budget. Over it you get 429 with a Retry-After header in seconds:

rate limit exceeded: 60 requests/min per app; retry after 1s

At most 4 requests per database run at once; more wait their turn rather than being refused.

There are no storage quotas yet.

Size and time limits

LimitValue
Request body1 MiB, so every field too
One statement5 seconds (408 past it)
Rows a query may match10,000 (422 past it; set a limit to page)
Values in one query’s filter65,534 (400 past it)
Text values and ridsAny string without a NUL character (\u0000); a rid must not be empty
Versions history returnsThe first 10,000
Table names51 characters
Column names63 characters