Errors: try, catch, raise

Meta: positioning matters: errors are not for control flow. Open with "Dang uses errors for errors — recoverable failures across boundaries — not for null or expected branches."

Dang uses errors for errors — recoverable failures across boundaries — not for null or expected branches.

Raising

raise "something went wrong"
raise NotFoundError(message: "user gone", resource: "User")

The Error interface

interface Error {
  pub message: String!
}

Catching

try {
  validate(name)
} catch {
  err => "fallback: " + err.message
}

Type-pattern catches

try { ... } catch {
  v: ValidationError => v.field
  n: NotFoundError => n.resource
  e: Error => e.message     # interface pattern = typed catch-all
  err => err.message        # bare catch-all, err: Error!
}

Propagation

When to raise vs. return null

Meta: a small "when to raise vs. when to return null" table would help here. The rule of thumb: raise when continuing would yield wrong results; return null when absence is normal.

situation use
absence is a normal, expected outcome return null (nullable type)
caller routinely branches on the result return null / a result value
continuing would produce wrong results raise
failure crosses a boundary (validation, HTTP/GraphQL, contract) raise

Common patterns

Anti-patterns