symfonic.services.shadow.errors¶
errors ¶
The shadow / record-replay error taxonomy.
Each class answers a distinct operational question, because each leads to a different response: "did the run touch a port nobody classified?" (stop shipping shadow evidence until it is classified), "did it reach an opaque extension?" (this capability needs the non-shadow cutover path), "did an extension declared port-mediated escape its ports?" (the trust declaration was wrong and the suppression claim is void), "was this invocation ineligible for capture?" (proceed unrecorded), "would this comparison duplicate a real effect?" (refuse the comparison outright).
A caller that had to parse a message to tell those apart would eventually parse it wrong, so they are types.
CaptureRefusedError ¶
Bases: ShadowError
Safe capture could not be established; the invocation is unrecorded.
Recording fails closed. Callers catch this and continue serving the tenant's request — never the other way round.
ComparisonUnsafeError ¶
Bases: ShadowError
TM-29d — this comparison would duplicate an externally visible effect.
CutoverPathError ¶
Bases: ConfigurationError
Cutover evidence was filed under a path the capability may not use.
IncompleteClassificationError ¶
Bases: ConfigurationError
An effect family the architecture declares has no classified port.
MisdeclaredExtensionError ¶
Bases: ShadowAbortedError
An extension declared port-mediated performed a direct effect.
The declaration was wrong; every suppression claim that depended on it is withheld, and the extension is demoted to opaque.
OpaqueExtensionError ¶
Bases: ShadowAbortedError
An opaque tool, plugin, or contributed stage is non-shadowable.
Raised before the extension runs. Pretending an opaque extension's effects were suppressed is the failure mode this type exists to prevent.
RecordingAccessError ¶
Bases: ShadowError
TM-29a — an actor read (or tried to read) beyond its authorization.
RecordingStoreError ¶
Bases: ShadowError
The recording store refused a write it cannot govern.
ShadowAbortedError ¶
Bases: ShadowError
The shadow run stopped before (or instead of) performing an effect.
Raised on its own when a run is aborted for a reason that has no more specific type; the fail-closed subclasses below are the usual cause.
ShadowEffectDenied ¶
Bases: ShadowError
A classified DENY port refused an effect inside a shadow run.
Not an abort: denial is the port doing its job. The caller decides whether the denial is fatal to the scenario it was exercising.
ShadowError ¶
Bases: SymfonicError
Root of the shadow/replay taxonomy. Never raised directly.
TrustDeclarationError ¶
Bases: ConfigurationError
A trust-class assignment does not meet the port-mediated bar.
A ConfigurationError on purpose: a self-declared or unreviewed
port-mediated claim is a misconfiguration of the evidence pipeline, and
adopters already catch that taxonomy.
UnclassifiedEffectError ¶
Bases: ShadowAbortedError
SEC-FCP-5 — an effect was attempted through a port nobody classified.
Fail-closed by construction: an unclassified port is not "probably safe", it is a hole in the evidence, and the run that found it is void.
UndigestibleValueError ¶
Bases: ShadowError
A value's identity cannot be established, so no digest is produced.
The digest is the comparator's only notion of "same request". A value the canonicaliser cannot see inside would digest identically to every other instance of its type, and the comparator would read that collision as parity. Refusing is the fail-closed answer: an unrecorded or aborted comparison is recoverable, a false parity claim is not.