Skip to content

symfonic.core.contracts.errors

errors

Phase 3 error types plus the facade error taxonomy (T2.1.1 FERR-1…FERR-3).

SymfonicError is the single root every simple-facade error subclasses. It is introduced here — today's nearest analogue of the future kernel.contracts and already the home of CapabilityError — so the E1/E3 move into the kernel is a relocation rather than a redesign.

Two compatibility rules govern this module and are load-bearing:

  • SymfonicAgentError is re-parented onto SymfonicError, never renamed. Widening a class's ancestry cannot narrow what an existing except clause catches, so every catalogued except SymfonicAgentError behaves identically (FERR-1 / ERR-7).
  • ConfigurationError is promoted here from symfonic.core.graph, which now re-imports this same object. One class, two import paths — a second class of the same name would be exactly the trap FERR-2 forbids.

BuildResult dataclass

BuildResult(content: str, status: Literal['success', 'partial', 'failed'], warnings: tuple[BuildWarning, ...] = (), degraded_sections: tuple[str, ...] = ())

Result of a prompt build operation.

BuildWarning dataclass

BuildWarning(section: str, type: Literal['degraded', 'critical_error'], message: str)

Warning emitted during prompt build.

CapabilityError

CapabilityError(capability: str, cause: str, code: str, metadata: dict[str, Any] | None = None)

Bases: Exception

Error from an external capability (fetch, search, etc.).

Not a frozen dataclass — exceptions must support traceback assignment. Fields are set as regular attributes for compatibility.

Source code in symfonic/core/contracts/errors.py
def __init__(
    self,
    capability: str,
    cause: str,
    code: str,
    metadata: dict[str, Any] | None = None,
) -> None:
    self.capability = capability
    self.cause = cause
    self.code = code
    self.metadata = metadata or {}
    super().__init__(str(self))

ConfigurationError

Bases: SymfonicError

Raised when capability flags are violated at compile time.

Promoted from symfonic.core.graph (FERR-2). The facade raises it for bad constructor input, an unregistrable capability, and a provider that cannot satisfy a requested structured output — always before the first provider call (FERR-3).

ContractViolationError

Bases: SymfonicError

Raised when a documented facade contract is misused.

W1's only use is LIF-4: run()/stream() after close(). Closed is terminal and there is no reopen, so continuing would be a silent lie about the agent's state.

A PRE_MODEL, POST_MODEL, PRE_TOOL, POST_TOOL or FINALIZE stage handler may preserve an adopter-defined refusal type across the :class:symfonic.Agent boundary by subclassing this error and setting preserve_contract_identity = True on the subclass. Without that explicit opt-in, a handler exception is wrapped in this base type and retained as its __cause__. PROMPT_ASSEMBLY always wraps, including opted-in subclasses, through its separate prompt-assembly failure guard.

ServiceTimeoutError

Bases: SymfonicError, TimeoutError

An internal deadline elapsed (ERR taxonomy §1, CXL-6).

Two ancestries, both load-bearing. SymfonicError puts it inside the taxonomy an adopter catches; the stdlib TimeoutError means every except TimeoutError already written around a run keeps working, and asyncio.timeout's own failure mode translates into it rather than being wrapped in something a caller has never heard of.

It exists so "the backend hung" stays distinguishable from "the caller stopped caring": external cancellation raises CancelledError and is never converted into this, and this is never converted into that (CXL-6). ServiceError — its eventual parent in the T1.2.3 taxonomy — has not landed yet, so it parents on the interim root exactly like the classes above it, and re-parenting later cannot narrow any existing except.

SymfonicError

Bases: Exception

Root of the simple-facade error taxonomy (FERR-1).

Never raised directly. It exists so an adopter can write one except SymfonicError and catch every failure the facade is documented to raise — and nothing else. Provider SDK exceptions deliberately do not inherit from it: W1 propagates them unchanged rather than shipping a half-built wrapper that swallows the provider's own diagnostics (FERR-4).