Note: This file is hand-maintained. When the error hierarchy in
lib/errors.tsor the protocol code map inprotocol/binary-server.tschanges, update this document to match. Ports the request in geldata/gel#6648.
Disc surfaces errors in three layers:
DiscError raised by the parser, compiler, runtime, and protocol layers. These are what your code catches when calling Disc as a library.POST /query and POST /transaction/* return, with a string extensions.code and, for failures inside PostgreSQL, the SQLSTATE. The TypeScript SDK turns these into its own error classes.When you receive an error from a Disc client SDK, the code field is a protocol code (column 2 below); the name field corresponds to a Disc error class (column 1).
A failed POST /query answers { "errors": [{ "message", "extensions": { "code", … } }] }. The HTTP status is 400 for every query error except an access policy violation or a refused persistent configure (403), 408 for a timeout and 500 for an internal error; 413 (body too large), 401/403, 404 and 429 come from the HTTP layer with a plain { "error": "…" } body.
extensions.code |
Status | Meaning | Aborts an open transaction? |
|---|---|---|---|
VALIDATION_ERROR |
400 | Malformed request, or a variable problem: a missing variable (Missing variable: the query uses $x, …), an unknown one (Unknown variable: $typo was provided, …), or an invalid bytes value (not base64 / \x hex). Nothing reached the database. |
No |
SYNTAX_ERROR |
400 | The query does not start with a valid statement or has unbalanced braces. | No |
PARSE_ERROR |
400 | The EdgeQL parser rejected the query. | No |
COMPILATION_ERROR |
400 | The compiler rejected it: unknown type or function, unresolvable conflict target, <bytes> from JSON… |
No |
READ_ONLY_MODE |
400 | The server is read-only and the query contains a write (anywhere in it, including nested in with/for/select (…)). |
No |
QUERY_TOO_LARGE |
400 | Query text over 100 KB. | No |
EXECUTION_ERROR |
400 | PostgreSQL rejected the statement. extensions.sqlState is the SQLSTATE; constraint, table and detail are present when PostgreSQL sent them. |
Yes |
CONFIGURATION_ERROR |
400 | configure of a key outside the allowlist (unrecognized configuration parameter), or configure session of a system-level key (use configure system). Nothing reached the database. |
No |
DISABLED_CAPABILITY |
403 | Persistent configure (system, database, instance) by a caller who isn’t an administrator (cannot execute configuration commands). |
No |
ACCESS_POLICY_ERROR |
403 | An object the statement writes fails the type’s access policies: access policy violation on insert of <Type> (or update), SQLSTATE 42501. Nothing was written. |
Yes |
TIMEOUT |
408 | The request exceeded DISC_REQUEST_TIMEOUT. The statement’s outcome is unknown. |
Yes |
INTERNAL_ERROR |
500 | The handler crashed. | Yes |
TRANSACTION_ABORTED |
409 | POST /transaction/commit on a transaction poisoned by an earlier failure; it has been rolled back and the id is gone. |
— |
WARNING |
200 | Not an error (dry-run mode). | No |
SQLSTATEs worth matching on: 23505 unique violation (a duplicate on an exclusive constraint or unique index; constraint names it), 23503 foreign-key violation, 23514 check violation (a regexp, one_of, min_value… constraint on a property or scalar type, or an expression on constraint), 21000 cardinality violation (assert_single found several, or an object cast <T><uuid>$p named an id no T has: 'default::T' with id '…' does not exist), 40001 serialization failure and 40P01 deadlock (retry the whole transaction). The SDK maps these to UniqueViolationError, ForeignKeyViolationError, ConstraintViolationError, CardinalityViolationError, SerializationFailureError and DeadlockError, all instanceof DiscQueryError with .sqlState.
A value PostgreSQL can’t take fails with PostgreSQL’s message worded with Gel’s type names, as Gel words it: invalid input syntax for type std::int64: "x" (22P02), value "99999" is out of range for type std::int16 and std::int64 out of range (22003), std::cal::local_date/std::cal::local_time field value out of range: "…" (22008). The SQLSTATE is PostgreSQL’s, unchanged: match on it, or on the SDK’s error class, rather than on the message.
A cast of a str fails as Gel’s does: <bool>'t' is invalid input syntax for type std::bool: 't' (only true and false are read), <Color>'Purple' is invalid input value for enum 'default::Color': "Purple" (22P02), and text that isn’t ISO 8601 cast to datetime, cal::local_datetime, cal::local_date or cal::local_time is invalid input syntax for type std::datetime: 'x' (22007) with Gel’s hint, Please use ISO8601 format. Example: 2010-12-27T23:59:59-07:00. …. The hint is extensions.hint over HTTP, the hint attribute of the binary protocol’s error, and hint on the SDK’s DiscQueryError. <bytes>'x' is the compile error cannot cast 'std::str' to 'std::bytes'.
extensions.queryHash on every response is the SHA-256 hex digest of the query text.
All errors inherit from the abstract base class DiscError. Every error carries an optional ErrorContext (source snippet, source location, hint) and renders a fully-formatted multi-line message via formatError().
| Class | Description | Notes |
|---|---|---|
DiscError (abstract) |
Base class for every Disc error. Holds context: { source?, location?, hint? } and provides formatError(). |
Never thrown directly. |
SyntaxError |
Parse-time syntax error — produced by the SDL or EdgeQL lexers/parsers when the input is structurally invalid. | Always carries location. |
SchemaError |
Schema validation error — references a type/link/constraint that doesn’t exist or violates schema rules. | Raised during schema validation and migration. |
QueryError |
EdgeQL semantic error — path resolution, type inference, or shape construction failure. | The general-purpose query error. |
CompilationError |
EdgeQL → SQL compilation failure that isn’t a user-facing schema or syntax problem. | Internal-ish; usually surfaces during query planning. |
ConfigurationError |
configure of a key outside the allowlist, or configure session of a system-level key (a subclass of CompilationError). |
Maps to ConfigurationError. |
DisabledCapabilityError |
Persistent configure by a caller who isn’t an administrator. |
Maps to DisabledCapabilityError. |
InvalidValueError |
A value the operation can’t take, found while compiling (a subclass of CompilationError): datetime_get(dt, 'fortnight'). |
Maps to InvalidValueError. |
ValidationError |
Value-level validation failure (e.g. constraint, type cast). | Maps to InvalidValueError on the wire. |
InternalError |
A bug in Disc itself. The constructor automatically appends (this is a bug — please file an issue at https://github.com/systemsoft/disc/issues) to the message. The append is idempotent: re-wrapping doesn’t duplicate the suffix. |
Ports geldata/gel#930. |
ConnectionError |
Failed to reach PostgreSQL, or the connection was lost mid-query. | Maps to BackendUnavailableError. |
MigrationError |
Migration generation or application failure. | Carries the offending DDL when available. |
DatabaseRegistryError |
A registry-level failure: branch not found, branch already exists, registry corrupted. | Always non-recoverable — no automatic retry. |
DatabaseExecutionError |
A PostgreSQL error wrapped with the originating SQL. Has fields sql: string and cause: Error. |
The PostgreSQL SQLSTATE (or the wrapped Disc error) determines the protocol code. |
QueryTimeoutError |
Query exceeded its configured timeout. Has fields sql: string and timeoutMs: number. Message is always "Query timed out after Nms". |
Maps to QueryTimeoutError. |
ResultCardinalityMismatchError |
A binary-protocol client asked for one result (querySingle, queryRequiredSingle, their JSON forms) of a query that returns more than one: the query has cardinality MANY which does not match the expected cardinality ONE. |
Maps to ResultCardinalityMismatchError. |
TransactionAbortedError |
commitTransaction() was called on a transaction that an earlier failed statement had aborted; the manager rolled it back and removed it. Has transactionId. |
409 TRANSACTION_ABORTED over HTTP. |
These are the 32-bit codes Disc returns to binary-protocol clients. They are Gel’s codes (edb/api/errors.txt), so existing Gel SDKs pick the right error class — and know which errors to retry. Defined in protocol/binary-server.ts as GEL_ERROR_CODES.
The high byte is the family; subsequent bytes narrow within the family.
| Code | Hex | Name |
|---|---|---|
| 16777216 | 0x01000000 |
InternalServerError |
| 33554432 | 0x02000000 |
UnsupportedFeatureError |
| 50331648 | 0x03000000 |
ProtocolError |
| 50528256 | 0x03030000 |
ResultCardinalityMismatchError |
| 50594304 | 0x03040200 |
DisabledCapabilityError |
| Code | Hex | Name |
|---|---|---|
| 67108864 | 0x04000000 |
QueryError |
| 67174400 | 0x04010000 |
InvalidSyntaxError |
| 67174656 | 0x04010100 |
EdgeQLSyntaxError |
| 67174912 | 0x04010200 |
SchemaSyntaxError |
| 67239936 | 0x04020000 |
InvalidTypeError |
| 67240192 | 0x04020100 |
InvalidTargetError |
| 67240193 | 0x04020101 |
InvalidLinkTargetError |
| 67305472 | 0x04030000 |
InvalidReferenceError |
| 67305473 | 0x04030001 |
UnknownModuleError |
| 67305477 | 0x04030005 |
UnknownDatabaseError |
| 67371008 | 0x04040000 |
SchemaError |
| 67436544 | 0x04050000 |
SchemaDefinitionError |
| 67436809 | 0x04050109 |
InvalidConstraintDefinitionError |
| 67437061 | 0x04050205 |
DuplicateDatabaseDefinitionError |
| 67502336 | 0x04060100 |
IdleSessionTimeoutError |
| 67502592 | 0x04060200 |
QueryTimeoutError |
| 67504641 | 0x04060a01 |
IdleTransactionTimeoutError |
| Code | Hex | Name |
|---|---|---|
| 83886080 | 0x05000000 |
ExecutionError |
| 83951616 | 0x05010000 |
InvalidValueError |
| 83951617 | 0x05010001 |
DivisionByZeroError |
| 83951618 | 0x05010002 |
NumericOutOfRangeError |
| 83951619 | 0x05010003 |
AccessPolicyError |
| 84017152 | 0x05020000 |
IntegrityError |
| 84017153 | 0x05020001 |
ConstraintViolationError |
| 84017154 | 0x05020002 |
CardinalityViolationError |
| 84017155 | 0x05020003 |
MissingRequiredError |
| 84082688 | 0x05030000 |
TransactionError |
| 84082945 | 0x05030101 |
TransactionSerializationError |
| 84082946 | 0x05030102 |
TransactionDeadlockError |
| Code | Hex | Name |
|---|---|---|
| 100663296 | 0x06000000 |
ConfigurationError |
| Code | Hex | Name |
|---|---|---|
| 117440512 | 0x07000000 |
AccessError |
| 117506048 | 0x07010000 |
AuthenticationError |
| Code | Hex | Name |
|---|---|---|
| 134217728 | 0x08000000 |
AvailabilityError |
| 134217729 | 0x08000001 |
BackendUnavailableError |
| Code | Hex | Name |
|---|---|---|
| 150995200 | 0x09000100 |
UnsupportedBackendFeatureError |
Performed by mapErrorToGelCode() in protocol/binary-server.ts. An error PostgreSQL raised is mapped by its SQLSTATE, as Gel does: 23502 → MissingRequiredError, 23000/23001/23503/23505/23514/23P01 (unique, foreign key, check, exclusion) → ConstraintViolationError, 21000 → CardinalityViolationError, 22012 → DivisionByZeroError, 22003/22015 → NumericOutOfRangeError, other 22xxx → InvalidValueError, 42501 (an access policy violation) → AccessPolicyError, 40001 → TransactionSerializationError, 40P01 → TransactionDeadlockError, 25006/25P02 → TransactionError, 57014 → QueryTimeoutError, 08xxx and 57P01–57P03 → BackendUnavailableError, 0A000 → UnsupportedBackendFeatureError; any other SQLSTATE → InternalServerError. Otherwise by class:
| Disc class | Gel code |
|---|---|
SyntaxError |
EdgeQLSyntaxError (0x04010100) |
SchemaError |
SchemaDefinitionError (0x04050000) |
InvalidReferenceError |
InvalidReferenceError (0x04030000) |
InvalidValueError |
InvalidValueError (0x05010000) |
ConfigurationError |
ConfigurationError (0x06000000) |
DisabledCapabilityError |
DisabledCapabilityError (0x03040200) |
CompilationError, QueryError |
QueryError (0x04000000) |
ValidationError |
InvalidValueError (0x05010000) |
ResultCardinalityMismatchError |
ResultCardinalityMismatchError (0x03030000) |
DatabaseExecutionError |
Its cause’s code, else InternalServerError |
QueryTimeoutError |
QueryTimeoutError (0x04060200) |
ConnectionError |
BackendUnavailableError (0x08000001) |
InternalError, anything else |
InternalServerError (0x01000000) |