Skip to content
GantryCD

Error Handling

GantryCD uses typed domain errors plus handler-side HTTP mapping.

Domain Errors

Primary typed errors live in pkg/domain/errors.go:

  • ValidationError
  • UnauthorizedError
  • ForbiddenError
  • NotFoundError
  • ConflictError
  • InternalError
  • UnsupportedRuntimeProviderError
  • ServiceUnavailableError

Rules:

  • services and repositories return domain errors
  • handlers inspect them with errors.As
  • do not add IsXxx helper functions

Sentinels

Some packages also expose sentinels for infrastructure concerns, for example storage-layer not-found cases. Translate those to domain errors at the appropriate boundary instead of leaking infrastructure semantics outward.

Returning Errors

Prefer typed returns such as:

return domain.NewValidationError("field", "reason")
return domain.NewForbiddenError("access denied")
return domain.NewConflictError("runner group still has active runners")

Wrap unexpected lower-level failures with context:

return fmt.Errorf("failed to list pull requests: %w", err)

Use InternalError only when the caller should see a domain-level internal failure rather than a plain wrapped infrastructure error.

Checking Errors

Use errors.As, not string matching:

var notFoundErr *domain.NotFoundError
if errors.As(err, &notFoundErr) {
    ...
}

HTTP Mapping

Handlers own error-to-status mapping.

Typical mapping:

  • validation → 400
  • unauthenticated → 401
  • forbidden → 403
  • not found → 404
  • conflict → 409
  • unsupported runtime provider → 422
  • unexpected/internal → 500
  • service unavailable → 503

Transient PostgreSQL conflicts are mapped centrally too: lock_timeout (SQLSTATE 55P03) and deadlock_detected (40P01) return 503, while statement_timeout (57014) returns 504.

Runner-facing handlers still follow the same pattern, but a few JWT/path mismatches are rejected directly with 403 before service invocation.

Logging

Log with context, then return the error upward. Avoid double-wrapping or emitting multiple noisy logs for the same expected domain failure.