Sealcraft's exception hierarchy is intentionally granular so applications can render meaningful error pages without a generic 500.
Hierarchy
All Sealcraft exceptions extend Crumbls\Sealcraft\Exceptions\SealcraftException.
| Exception | Thrown when | Typical handling |
|---|---|---|
ContextShreddedException |
Read or write against a context whose DEK has been shredded | Render "record destroyed at user request" (410 Gone) |
DecryptionFailedException |
Ciphertext fails authentication, or unwrap fails at the cipher layer | Treat as data corruption / tampering; alert security; do not retry |
InvalidContextException |
Context change on a model when auto_reencrypt_on_context_change=false, OR per-row model with an empty row-key column |
Application bug / incomplete backfill; fix and redeploy |
KekUnavailableException |
KMS provider is unreachable, throttled, or the KEK is disabled | Retry with exponential backoff; page oncall if persistent |
Distinguishing shred from corruption
ContextShreddedException and DecryptionFailedException are siblings, not parent/child. This lets you render completely different UX:
try {
$patient = Patient::findOrFail($id);
return view('patient.show', compact('patient'));
} catch (ContextShreddedException $e) {
return response()->view('patient.shredded', [], 410);
} catch (DecryptionFailedException $e) {
Log::alert('Decryption failed', ['patient_id' => $id]);
abort(500);
}
Do not catch broadly
catch (\Exception $e) around a Sealcraft call swallows KekUnavailableException (transient -- should retry) and DecryptionFailedException (not transient -- should alert). Catch the specific subclasses.
Logging
No Sealcraft exception message or context includes plaintext data. It is safe to log the full message, exception class, and stack trace. The context property on SealcraftException subclasses is the canonical context bytes -- suitable for correlation but not sensitive.
Contributors
Thank you to everyone who has contributed to this package. Every pull request, bug report, and idea makes a difference.