Errors

July 10, 2026 ยท View on GitHub

Every client method that talks to the server reports failure via an intent(out) integer :: stat argument. On success stat is MDB_OK (0); on failure it is a negative category code. An optional errmsg carries the human-readable detail.


The code set

CodeConstantHTTP statusCause
0MDB_OK-Success.
-1MDB_ERR_AUTH401, 403Missing, malformed, or rejected Authorization header. Bad token or basic-auth credentials.
-2MDB_ERR_NOT_FOUND404Unknown table name, or a row that does not exist.
-3MDB_ERR_CONFLICT409Unique constraint violation: duplicate primary key, or a column-level uniqueness/enum violation.
-4MDB_ERR_QUERY400, 5xxMalformed request body, unknown column id, a server-side planner/execution error, or a response that exceeded the size cap.
-5MDB_ERR_NETWORK-The HTTP request itself failed: connection refused, DNS error, timeout, broken pipe.
-6MDB_ERR_JSON-The server returned a response that could not be decoded as JSON when JSON was expected.
-8MDB_ERR_INVALID_ARG-A caller-supplied payload (columns, ops, query body) was not valid JSON.

Matching

Use a normal if/select case on stat:

call db%put('orders', '[1,1,2,"Alice"]', stat, errmsg)
select case (stat)
case (MDB_ERR_CONFLICT)
  print *, 'duplicate row, skipping: ', trim(errmsg)
case (MDB_ERR_AUTH)
  print *, 'bad credentials: ', trim(errmsg)
  error stop 1
case (MDB_ERR_NETWORK)
  print *, 'daemon unreachable, will retry: ', trim(errmsg)
case (MDB_OK)
  ! success
case default
  print *, 'unexpected: ', trim(errmsg)
end select

Reading the detail

errmsg (when present) is set to the server's own error text when the server produced one:

duplicate primary key value

For server errors the daemon wraps detail in an envelope ({"error":{"message":..., "code":..., "op_index":...}}). The client extracts message into errmsg. op_index (when present) identifies which op in a batch transaction triggered the rollback.

Recoverable vs not

CodeRecoverable?Pattern
MDB_ERR_NETWORKYes - retry after backoffTransient; the daemon may have restarted.
MDB_ERR_CONFLICTSometimes - re-read, reconcile, retryThe data changed under you. Re-fetch and decide.
MDB_ERR_AUTHNo - fix credentials and reconnectStale token, wrong password.
MDB_ERR_NOT_FOUNDNo - check the table/row idProgramming error or race.
MDB_ERR_QUERYNo - fix the requestMalformed body, bad column id, etc.
MDB_ERR_JSONNo - protocol mismatchLikely a server version skew.

Retrying safely

For MDB_ERR_NETWORK, retry with backoff. If the operation is a write, pass an idempotency key so a replayed request is deduplicated on the server:

do attempt = 1, 3
  call db%transaction(ops_json, results_json, stat, errmsg, &
                      idem_key='put-' // key_suffix)
  if (stat /= MDB_ERR_NETWORK) exit
  call sleep_ms(200 * (2 ** (attempt - 1)))
end do
if (stat /= MDB_OK) error stop 1

See transactions.md for more on idempotency keys.

The size cap

The client rejects any response body larger than 256 MiB (MDB_MAX_RESPONSE_BYTES) with a MDB_ERR_QUERY error. This is a guard against runaway queries exhausting memory. If you hit it, narrow your query (add a limit, project fewer columns, or page with SQL LIMIT/OFFSET).

Next steps