symfonic.capabilities.human.errors¶
errors ¶
The human-interaction error taxonomy, split by what an operator must do.
Every refusal carries a code: a short, transport-neutral word. The legacy
engine raised one exception type and discriminated on exactly these strings, so
a route that mapped "gone" to 410 keeps working โ but the type now carries
the distinction too, because except SymfonicAgentError could not tell "the
user took too long" from "somebody tampered with this token".
The splits that matter:
- expired vs unauthorized (SEC-PTK-1/2). One deserves "ask again", the other deserves a security alert. Neither is a subclass of the other, and a test asserts it.
- replayed vs unauthorized (SEC-PTK-3). A replay is a token that was real. Reporting it as a forgery would page somebody for a double-clicked button.
- checkpoint lost vs not found. The first is a deployment fact (a volatile checkpointer restarted); the second is a missing row on a durable backend. Collapsing them is what sent operators chasing token bugs that were restarts.
- operated-platform-only. Asking a library-mode deployment for a drain proof is a category error (LIB-TL-4), not a failure to produce one.
CallBindingError ¶
Bases: PauseTokenError
The token was minted for another call of the same run (HK2).
ask_user correlates on tool_call_id and a registered interrupt on
interrupt_id; this is the refusal for both. A run that paused twice --
routine for an onboarding agent still working a checklist -- has two live
tokens whose run, session and scope are all identical, so the call id is the
only axis that separates them. Verifying the other three and not this one
would let the second question's answer be recorded against the first.
CheckpointLostError ¶
ConsumptionDurabilityError ¶
DuplicateInteractionError ¶
Bases: InteractionRegistrationError
Two registrations claim one name; there is no defensible winner.
HumanInteractionError ¶
Bases: Exception
Base for everything this capability raises.
InteractionConfigurationError ¶
Bases: HumanInteractionError, ValueError
The deployment wired something that cannot work, before any run starts.
InteractionRegistrationError ¶
Bases: HumanInteractionError, ValueError
A named interaction is not describable, so nothing may register it.
OperatedPlatformOnlyError ¶
PauseCheckpointNotFoundError ¶
PauseTokenError ¶
PauseTokenExpiredError ¶
PauseTokenReplayedError ¶
PauseTokenUnauthorizedError ¶
Bases: PauseTokenError
The token does not authenticate: forged, tampered, or unreadable.
One refusal that reaches here is not about the token at all: when the
verification keyset is unavailable the signer must deny, and denying is all
it can do โ but telling the holder "your token is bad" while the secret
manager is down is a lie, and one their retry logic acts on. So the reason
may override the code (EMAP-6): the transport reads code and answers
503 + Retry-After instead of 401. The class is unchanged because the
security decision is unchanged; only the explanation is now honest.
Source code in src/symfonic/capabilities/human/errors.py
from_signer_refusal
classmethod
¶
The denial a failed verify becomes, carrying the reason's code.
Lives beside the class rather than at the call site so there is one place where "which refusals are about the token?" is answered, and so the token service does not have to know that a signer's dependency has a taxonomy at all.
Source code in src/symfonic/capabilities/human/errors.py
PayloadBindingError ¶
ResponseValidationError ¶
RetirementHorizonError ¶
RunBindingError ¶
Bases: PauseTokenError
The token was minted for another run of the same session (HK2).
Its own class rather than a SessionBindingError, because the two say
different things to an operator. A session mismatch is a caller answering
from the wrong conversation; a run mismatch is a caller answering the right
conversation's previous question -- a stale browser tab, a retried
request, a queue that replayed. Both refuse; only one of them suggests
somebody is looking at an old page.
ScopeBindingError ¶
SessionBindingError ¶
TokenLedgerError ¶
TokenTTLError ¶
Bases: HumanInteractionError, ValueError
A lifetime outside the configured bound. Never clamped, always refused.
TurnStateNotRecordedError ¶
Bases: HumanInteractionError
A recorded turn state does not describe the turn the token binds (HK2).
UnissuedTokenError ¶
Bases: PauseTokenError
The authoritative ledger never issued this token, so it does not exist.
A separate class from :class:PauseTokenUnauthorizedError even though both
deny: this one means the envelope was fine and the ledger still says no,
which is a deployment/routing question, not a forgery.
UnknownInteractionError ¶
Bases: HumanInteractionError, KeyError
Nothing is registered under that name.
Also a :class:KeyError because the shipped registry was a dict and an
adopter's except KeyError around a lookup is a reasonable thing to have.