Skip to content

Errors

Errors fall into three groups:

  • Database errors from Postgres, translated into a typed PostgresException hierarchy by every DbContext.
  • Builder and runtime errors thrown by gisila_orm itself before or after a statement runs.
  • Build-time errors from the schema parser and migration generator.
import 'package:gisila_orm/gisila.dart';
try {
await authors().insert({'name': 'Ada', 'email': email}).one(db.context());
} on PostgresUniqueViolationException catch (e) {
print('taken: ${e.details?['constraint']}');
} on PostgresException catch (e) {
print('database error ${e.sqlState}: ${e.message}');
}
abstract class PostgresException implements Exception {
final String message;
final String? sqlState;
final int? errorCode;
final String? query;
final Map<String, dynamic>? details;
}

Base class of every translated server error.

Field Description
message The server’s error message.
sqlState Five-character SQLSTATE code, such as 23505.
errorCode Always null for errors mapped from the driver.
query The SQL text that failed, without parameter values. null for errors raised while committing a transaction.
details Map with whichever of these the server sent: detail, hint, table, column, constraint, schema.

When to use: catch specific subclasses for errors you can handle (a unique violation means “already exists”), and PostgresException as a fallback.

Errors raised by the driver itself (connection refused, timeouts, closed pool) are not ServerExceptions and pass through untranslated.

PostgresErrorMapper produces these classes. Any other SQLSTATE becomes PostgresUnknownException.

SQLSTATE Class Parent
00000 PostgresSuccessException PostgresException
01xxx (listed codes) PostgresWarningException PostgresException
02000, 02001 PostgresNoDataException PostgresException
08000, 08001, 08003, 08004, 08006, 08007 PostgresConnectionException PostgresException
08P01 PostgresProtocolViolationException PostgresConnectionException
0A000 PostgresFeatureNotSupportedException PostgresException
21000 PostgresCardinalityViolationException PostgresException
22000, 22021, 22024, 22025, 2202E, 2202F, 2202H PostgresDataException PostgresException
22001 PostgresStringDataTruncationException PostgresDataException
22003 PostgresNumericValueOutOfRangeException PostgresDataException
22007, 22008 PostgresInvalidDatetimeException PostgresDataException
22012 PostgresDivisionByZeroException PostgresDataException
23000, 23001 PostgresIntegrityConstraintViolationException PostgresException
23502 PostgresNotNullViolationException PostgresIntegrityConstraintViolationException
23503 PostgresForeignKeyViolationException PostgresIntegrityConstraintViolationException
23505 PostgresUniqueViolationException PostgresIntegrityConstraintViolationException
23514 PostgresCheckViolationException PostgresIntegrityConstraintViolationException
23P01 PostgresExclusionViolationException PostgresIntegrityConstraintViolationException
24000 PostgresInvalidCursorStateException PostgresCursorException
25000-25008, 25P01, 25P02 PostgresInvalidTransactionStateException PostgresTransactionException
anything else PostgresUnknownException PostgresException

25P02 (“current transaction is aborted”) is what you get when you keep using a transaction after one of its statements failed.

These are exported and part of the hierarchy, but no SQLSTATE maps to them in 0.2.1. Errors such as serialization failures (40001), deadlocks (40P01), undefined tables (42P01) and syntax errors (42601) arrive as PostgresUnknownException; check sqlState.

PostgresSyntaxException, PostgresUndefinedTableException, PostgresUndefinedColumnException, PostgresInsufficientPrivilegeException, PostgresTransactionException, PostgresSerializationFailureException, PostgresDeadlockDetectedException, PostgresLockTimeoutException, PostgresSystemException, PostgresInsufficientResourcesException, PostgresDiskFullException, PostgresTooManyConnectionsException, PostgresProgramLimitExceededException, PostgresConfigFileException, PostgresAuthenticationException, PostgresInvalidAuthorizationException, PostgresInvalidPasswordException, PostgresCursorException, PostgresInternalException, PostgresSavepointException, PostgresPlPgSqlException, PostgresExternalRoutineException, PostgresOperatorInterventionException, PostgresQueryCancelledException.

Example: retry a serializable transaction.

Future<R> withRetry<R>(Future<R> Function() run, {int attempts = 3}) async {
for (var i = 1;; i++) {
try {
return await run();
} on PostgresException catch (e) {
final retryable = e.sqlState == '40001' || e.sqlState == '40P01';
if (!retryable || i == attempts) rethrow;
}
}
}
class PostgresErrorMapper {
static PostgresException mapError(String message, String? sqlState, int? errorCode,
String? query, Map<String, dynamic>? details);
static PostgresException fromServerException(ServerException e, {String? query});
}

The translation table above. Every DbContext calls fromServerException for you. Call it yourself only if you run statements through package:postgres directly and want the same exception types.

Exception Thrown by When
QueryNoRowsException Query.one No row matched.
QueryMultipleRowsException Query.one More than one row matched.
InsertReturnedNoRowsException InsertQuery.one Nothing came back (returning(false), or ON CONFLICT DO NOTHING skipped the row).
InvalidQueryBuilderException compile / run on mutations An insert with no rows, an update with no values, or an update or delete with no WHERE and no .all().
PreloadException Preloader A parent model doesn’t mix in Preloadable (regenerate your models).
ArgumentError InsertQuery.compile, onConflictDoUpdate, RawSql compile Rows with different columns; empty conflict target; placeholder/parameter mismatch.
StateError any query on a closed Database Database.close() was called.

Each has a message field.

Author? author;
try {
author = await authors().where(AuthorTable.email.eq(email)).one(ctx);
} on QueryNoRowsException {
author = null; // or use first(ctx), which returns null instead of throwing
}

Thrown when a named connection can’t be resolved (Database.connect, DatabaseConfig.defaultConnection, db.context('missing')) or an ssl_root_cert file can’t be loaded. Has a message field.

Exception Thrown by When
MigrationChecksumException MigrationManager.up Applied migrations were edited. ids lists them.
MigrationRollbackException MigrationManager.down A migration to roll back has no file in the discovered set, or an empty down SQL.
DestructiveMigrationException SchemaDiffer.generateMigrationFile, generate, migrate diff The diff would lose data and wasn’t allowed. changes lists it.
class SchemaValidationException implements Exception {
final List<SchemaError> errors;
bool get hasErrors;
String format({bool? color});
}
class SchemaError {
final String code;
final String message;
final SourceSpan span;
final SchemaErrorLevel level; // error | warning
final String? hint;
final List<String> notes;
}

Thrown by SchemaDefinition.fromYaml, fromFile and fromProject with every problem found. format() renders them with file, line, column, a caret under the offending token and the hint; color defaults to auto-detection (respecting NO_COLOR). Exported from package:gisila_orm/generators/schema_parser.dart. The error codes are listed in Schema YAML format.

Exception Description
DefaultValueException An invalid default: value when generating SQL. Field msg.
QueryException Legacy; not thrown by current code. Field msg.
ValidationException Legacy; not thrown by current code. Field errors.

package:gisila_orm/web.dart provides PostgresDbErrorMapper, which turns these exceptions into HTTP errors. See gisila integration.

See also: Database, contexts and transactions, Migrations API.