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. RescueConfium::Errorat minimum so non-Confium exceptions propagate. - String-matching
e.message: errors carry structureddetailsfor 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.