Skip to content

symfonic.platform.errors

errors

The platform taxonomy: one class per refusal, each with a stable code.

EMAP-2 forbids mapping on message text, so every class here carries the machine -readable code a transport keys on. The four refusals a derivation can produce are separate classes rather than one error with a field, because T3.4.5 asserts three distinct refusals on the resume path precisely so a transport can map them without parsing prose โ€” and a taxonomy that collapses them again would undo that at the layer above.

AuthenticationError

Bases: PlatformError

No derivable credentials. 401.

Never a fallback to a default, shared, or root tenant (SCOPE-4, SEC-FCP-1).

AuthorizationError

Bases: PlatformError

Authenticated, but not for this scope. 403.

Also raised by :meth:AuthenticatedPrincipal.narrowed on an attempted widening and by require_self on a redirect attempt (SCOPE-8/SEC-AUTHZ-2), because "you may not act on that tenant" is one answer however it was asked.

BudgetExceededError

BudgetExceededError(message: str, *, code: str = 'budget_exceeded', reason: str | None = None, retry_after: int = 3600, scope_key: str = '')

Bases: PlatformError

Admission refused by the budget port. 429 + Retry-After.

This class is BUD-2's whole point. The shipped path raises a generic error whose message starts "Budget exceeded:" and three routers branch on that prefix; here the class carries the decision, code distinguishes exhaustion from an unavailable ledger, and retry_after is a number rather than a hard-coded header string in three places.

Source code in src/symfonic/platform/errors.py
def __init__(
    self,
    message: str,
    *,
    code: str = "budget_exceeded",
    reason: str | None = None,
    retry_after: int = 3600,
    scope_key: str = "",
) -> None:
    super().__init__(message)
    self.code = code
    self.reason = reason
    self.retry_after = retry_after
    self.scope_key = scope_key

DestructiveConfirmationError

Bases: PlatformError

A destructive operation arrived without intent bound to the resolved scope.

ADM-5: the confirmation is compared against the derived tenant id, never against a value taken from the same request that supplied the confirmation โ€” otherwise the check confirms only that the caller can copy a string.

PlatformError

Bases: SymfonicError

Root of the platform taxonomy.

ScopeDerivationError

Bases: PlatformError

The credentials named a scope that is not well formed. 400.