Error handling

Confium failures raise typed error classes — parse failures, out-of-range indexes, threshold violations, and verification failures from the native extension all carry structured details. Rescue the specific class for the failure you’re handling; rescue Confium::Error for any Confium-originated failure as a catch-all.

For failures that are better reported than raised, prefer the result-object APIs (PathValidator, composite VerificationResult, MerkleTree#verify_consistency on a proof you trust).

The hierarchy

StandardError
└── Confium::Error
    ├── ParseError             # malformed PEM / DER / JSON input
    ├── ValidationError       # well-formed but invalid (e.g. expired cert)
    ├── VerificationError     # signature / proof / inclusion check failed
    ├── ThresholdError        # threshold protocol failure (e.g. bad share)
    ├── CryptoError           # underlying primitive failure
    ├── NotFoundError         # requested signer / cert / share not found
    ├── IndexError            # sequence number out of range
    ├── UnresolvedSignerError # required signer could not be resolved
    └── PolicyViolationError  # jurisdictional / FIPS policy violation

Every typed error carries:

  • message — human-readable description.
  • details — Hash with structured context (which check failed, with what counts).

Rescue patterns

Single error type

begin
  Confium::PKI::Certificate.from_pem(input)
rescue Confium::ParseError => e
  warn "Parse failed: #{e.message}"
  warn "Details: #{e.details.inspect}"
  # => { format: "pem", operation: "Certificate.from_pem", ... }
end

Threshold protocols raise ThresholdError with the counts in both accessors and details:

begin
  Confium::TC::Cmp20.sign(kg["shares"].first(2), 3, "msg")
rescue Confium::ThresholdError => e
  e.have_count  # => 2
  e.need_count  # => 3
end

Result objects instead of exceptions

Path and signature verification report through result objects:

result = Confium::PKI::PathValidator.validate(leaf, nil, root_cert)
result.valid?        # => Boolean
result.check_count   # => Integer

verification = Confium::Composite::Signature.new(components).verify(msg)
verification.all_verified?         # => Boolean
verification.per_component         # => { 0 => { "algorithm" => ..., "verified" => ... } }

Catch-all

begin
  do_confium_thing
rescue Confium::Error => e
  # Any typed Confium failure.
  warn "#{e.class}: #{e.message}#{e.details.inspect}"
end

Surfacing errors in HTTP responses

For Rack / Sinatra / Rails apps, map each typed error to an HTTP status. Recommended mapping:

Error class HTTP status
ParseError 400 Bad Request
ValidationError 400 Bad Request
NotFoundError 404 Not Found
UnresolvedSignerError 404 Not Found
VerificationError 422 Unprocessable Entity
ThresholdError 422 Unprocessable Entity
PolicyViolationError 403 Forbidden
CryptoError 500 Internal Server Error
IndexError 400 Bad Request

A Sinatra helper:

helpers do
  def confium_error_status(err)
    case err
    when Confium::ParseError, Confium::ValidationError, Confium::IndexError then 400
    when Confium::NotFoundError, Confium::UnresolvedSignerError then 404
    when Confium::VerificationError, Confium::ThresholdError then 422
    when Confium::PolicyViolationError then 403
    else 500
    end
  end
end

error Confium::Error do
  err = env["sinatra.error"]
  status confium_error_status(err)
  { error: err.class.name.split("::").last.downcase,
    message: err.message,
    details: err.details }.to_json
end

Inspecting details

The shape of details is per error class. Real shapes today:

# ParseError (malformed PEM)
{ format: "pem", operation: "Certificate.from_pem", component: "Confium" }

# IndexError (proof out of range)
{ index: 8, operation: "MerkleTree.consistency_proof", component: "Confium" }

# ThresholdError (CMP20 below threshold)
{ have_count: 2, need_count: 3, operation: "Cmp20.sign" }

# PolicyViolationError (key below jurisdictional minimum)
{ policy: :eu, violation: :key_too_small }

# PolicyViolationError (non-FIPS algorithm in FIPS mode)
{ policy: :fips, violation: :unapproved_algorithm }

Code that reads details should treat the keys as advisory and defensively check for presence — new keys may be added in any release.

Anti-patterns

  • Rescuing StandardError: too broad. Rescue Confium::Error at minimum so non-Confium exceptions propagate.
  • String-matching e.message: errors carry structured details for a reason. Inspect the Hash, not the prose.
  • Silently swallowing: at minimum log the error class and details. Silent rescues hide real bugs.
  • Reraising untyped: preserve the type — re-raise the same error or wrap it with a typed class carrying the original in details.