Errors
What is raised, and what each one means about the state you are in.
PsychError
exception
Root of everything Psych raises.
AccessDenied
exception
class AccessDenied(subject: 'str', reason: 'str') -> 'None'The consumer's Policy port refused, or narrowing removed the tool.
Carries which, because "you may not" and "that no longer exists for you" are different problems for whoever is reading the log.
RunNotFound
exception
class RunNotFound(run_id: 'str') -> 'None'Something went wrong at the Store port.
RunNotSuspended
exception
class RunNotSuspended(run_id: 'str', state: 'str') -> 'None'resume() was called on a Run that is not waiting for anything.
Typed rather than a bare ValueError so an HTTP layer can map it to a
status without matching on message text.
RunAlreadySettled
exception
class RunAlreadySettled(run_id: 'str', what: 'str') -> 'None'A Run that has reached its terminal record was asked to do more.
Raised by send(); interrupt() on a settled Run is a no-op by
contract, since the first abort is the one that counts.
RunEndedWithoutAnswer
exception
A Run a caller was reading ended without producing one.
Raised only by psych_runtime.stream_text(), which is a projection that
yields words and therefore has no way to show an ending that produced
none. Returning quietly would leave a UI with a blank reply and no error for
a Run whose log says exactly what went wrong, so this is the one place a
projection raises rather than renders.
Every other read path returns the ending as data instead, because they can:
status() names the lifecycle, answer() sets finished to False,
and report() carries the terminal state and the failure.
RunAborted
exception
class RunAborted(run_id: 'str', state: 'str') -> 'None'The Run stopped before answering: interrupted, or past its deadline.
state distinguishes them, which a caller usually needs: a user pressing
stop is not an incident and a deadline is.
RunFailed
exception
class RunFailed(run_id: 'str', failure: 'object | None' = None) -> 'None'The Run settled FAILED.
failure is the one recorded in the log, so a caller reports what
happened rather than a generic message about streaming.
SuspensionExpired
exception
class SuspensionExpired(run_id: 'str', reason: 'str') -> 'None'A suspended Run waited past its expiry and is settled as abandoned.
DESIGN.md §11: suspensions expire rather than waiting forever on a user who left.
SpecValidationError
exception
class SpecValidationError(issues: 'list[ValidationIssue]') -> 'None'A Spec was refused at publish.
DESIGN.md §4: validation happens at publish, never at run. A customer
waiting on a response is not the right place to discover a typo, so this is
raised by publish() and never by the agent loop.
ValidationIssue
class
class ValidationIssue(path: 'str', message: 'str') -> 'None'One problem with a Spec, naming exactly what is wrong and where.
Not an exception: a publish reports every issue at once rather than making the author fix one typo per attempt.
Attributes: path: where in the Spec the problem is, dotted from the root. message: what is wrong, in a sentence an author can act on.
StoreError
exception
Something went wrong at the Store port.
SeqConflict
exception
class SeqConflict(run_id: 'str', seq: 'int') -> 'None'Appending Record seq found one already there.
The single-writer rule (DESIGN.md §6 rule 3) says exactly one Attempt holds the lease and may append, so this means another writer exists. The losing Attempt aborts immediately rather than retrying at the next sequence, which would interleave two Attempts' records into one log.
LeaseLost
exception
class LeaseLost(run_id: 'str', worker_id: 'str') -> 'None'This Attempt no longer holds the lease it was working under.
Raised when a renewal is refused because another Worker reclaimed an expired lease. The Attempt stops immediately: anything it writes from here would be a second writer on a log that permits one.
DeadlineExceeded
exception
class DeadlineExceeded(run_id: 'str', deadline_seconds: 'float') -> 'None'A Run passed the deadline every Run has (DESIGN.md §8.4).
TransientError
exception
class TransientError(message: 'str', *, retry_after_seconds: 'float | None' = None) -> 'None'A failure the classifier judged worth retrying (DESIGN.md §8.6).
Retries draw on a per-Run budget rather than a per-call one, so a Run cannot retry forever by spreading failures across steps.
CorruptLog
exception
class CorruptLog(reason: 'CorruptionReason', run_id: 'str', seq: 'int | None', detail: 'str') -> 'None'A Run's log is in a state the protocol cannot produce.
Raised by the reducer, never caught by the runtime to work around. The Run fails and the error names the reason, the sequence and the Run so the writer bug can be found from the log alone.
Attributes: reason: which invariant broke. run_id: whose log it is. seq: the record the fold was looking at when it noticed. Not necessarily the record that is wrong, because the contradiction is between records, but it is where to start reading. detail: what specifically contradicts what.
CorruptionReason
enum
class CorruptionReason(*values)Why a log is impossible. Each member is a distinct writer bug.
| Member | Value |
|---|---|
MULTIPLE_OPEN_OPERATIONS | multiple_open_operations |
UNKNOWN_OPERATION | unknown_operation |
RECORD_AFTER_FINISH | record_after_finish |
NON_CONSECUTIVE_SEQ | non_consecutive_seq |
NON_CONSECUTIVE_ATTEMPT | non_consecutive_attempt |
QUEUE_AFTER_ABORT | queue_after_abort |
INVALID_QUEUE_CANCELLATION | invalid_queue_cancellation |
INCONSISTENT_STEP | inconsistent_step |
TOOL_CALL_MISMATCH | tool_call_mismatch |
DUPLICATE_TOOL_INVOCATION | duplicate_tool_invocation |
PROVISIONED_ENTRY_MISMATCH | provisioned_entry_mismatch |
INVALID_DEFERRED_HANDLE | invalid_deferred_handle |
INVALID_COMPACTION_REASON | invalid_compaction_reason |
INCONSISTENT_COST | inconsistent_cost |
CredentialNotFound
exception
class CredentialNotFound(scope: 'Scope', name: 'str') -> 'None'A Spec named a credential that has no value for this Scope.
Raised by a caller of SecretResolver.resolve (psych_runtime.tools.mcp
among them) rather than by the port itself, so that whether a missing
credential is fatal stays a decision the caller makes deliberately: Psych
never falls back to treating "no credential configured" as "connect
without one", which would be a much quieter failure than this.
BuilderError
exception
The builder was asked to produce a Spec it cannot construct.