# `sandloom.sdk.errors`

Exceptions a module may raise or catch.

Public API 1.0 (spec §2.2, Appendix C).

`from sandloom.sdk.errors import <name>`. Signatures in a terminal: `sandloom docs <name>`. Beginner names are the ones generated code and the examples use; any other name is advanced (mark its import line `# sandloom: advanced <reason>` in generated code and examples).

## Advanced

| Name | Kind | Summary |
| --- | --- | --- |
| [`AuditUnavailableError`](<#auditunavailableerror>) | exception | A `required` audit write could not be performed. |
| [`BlockUnavailableError`](<#blockunavailableerror>) | exception | The emergency-block mark could not be written / removed (cache outage). |
| [`ConsumerDefinitionError`](<#consumerdefinitionerror>) | exception | An invalid `@on` consumer (SANDLOOM-B016). |
| [`ContractArgumentError`](<#contractargumenterror>) | exception | The call's arguments do not bind to / validate against the signature (422). |
| [`ContractCallError`](<#contractcallerror>) | exception | A contract call failed with a declared status — identical locally and over RPC. |
| [`ContractCallInProgress`](<#contractcallinprogress>) | exception | A command with this idempotency key is still running (503, retry later). |
| [`ContractDefinitionError`](<#contractdefinitionerror>) | exception | An invalid `@contract` class or `@command` method (SANDLOOM-B018). |
| [`ContractIdempotencyConflict`](<#contractidempotencyconflict>) | exception | A command's idempotency key was reused with DIFFERENT arguments (409). |
| [`ContractIdempotencyKeyMissing`](<#contractidempotencykeymissing>) | exception | A command contract was called without its idempotency/operation key. |
| [`ContractIdempotencyUnavailable`](<#contractidempotencyunavailable>) | exception | The command dedupe store is unreachable: the command is refused (503). |
| [`ContractResultNotReplayable`](<#contractresultnotreplayable>) | exception | A retried command already completed but its result was too large to keep (409). |
| [`DeclarationError`](<#declarationerror>) | exception | Base class of the L1 declaration refusals. |
| [`EventDefinitionError`](<#eventdefinitionerror>) | exception | An invalid `Event` class, or `.publish()` on a view (SANDLOOM-B015; a view's `.publish()` is SANDLOOM-C025 statically). |
| [`EventMetaUnavailable`](<#eventmetaunavailable>) | exception | `event.meta` read on an event that was not delivered. |
| [`MissingTenantError`](<#missingtenanterror>) | exception | A tenant-scoped operation ran with no tenant bound in the context. |
| [`ModuleDiscoveryError`](<#modulediscoveryerror>) | exception | Discovery refused the module: a declaration in the wrong place, two declarations claiming one name (SANDLOOM-B006). |
| [`ModuleRegistrationConflict`](<#moduleregistrationconflict>) | exception | A buffered registration would conflict with live kernel state. |
| [`ProjectionDefinitionError`](<#projectiondefinitionerror>) | exception | An invalid `Projection` (SANDLOOM-B012). |
| [`ReadContractWriteError`](<#readcontractwriteerror>) | exception | A read contract tried to write (its UoW is always rolled back). |
| [`RemoteServiceUnavailable`](<#remoteserviceunavailable>) | exception | A remote service binding exists, but the provider cannot serve the call. |
| [`RoutePolicyError`](<#routepolicyerror>) | exception | A route is both `public()` and authenticated (SANDLOOM-B008). |
| [`SagaDefinitionError`](<#sagadefinitionerror>) | exception | A saga definition is malformed or does not match its contracts. |
| [`ServiceNotFound`](<#servicenotfound>) | exception | Base error for service registry. |
| [`ServiceUnavailableInDeployment`](<#serviceunavailableindeployment>) | exception | A contract is neither provided locally nor bound to a remote provider. |
| [`TaskEnqueueConflict`](<#taskenqueueconflict>) | exception | An HTTP exception you can raise in your own code to show errors to the client. |
| [`TaskEnqueueUnavailable`](<#taskenqueueunavailable>) | exception | An HTTP exception you can raise in your own code to show errors to the client. |
| [`TaskEnqueueUncertain`](<#taskenqueueuncertain>) | exception | An HTTP exception you can raise in your own code to show errors to the client. |
| [`TenantMismatchError`](<#tenantmismatcherror>) | exception | A tenant-owned object does not belong to the bound tenant. |
| [`UnitOfWorkRequiredError`](<#unitofworkrequirederror>) | exception | A guaranteed write-path effect was requested outside a Unit of Work. |
| [`UnsafeTenantOperationError`](<#unsafetenantoperationerror>) | exception | An ORM operation could bypass or mutate tenant ownership. |

## Advanced names

### `AuditUnavailableError`

```
class AuditUnavailableError(RuntimeError)
```

A `required` audit write could not be performed.

Raised by `record_audit` (`required` is the default since 0.7) when there is no UoW scope, the backend has no audit store, or the actor id is not a UUID — a security/compliance action must fail loudly rather than silently skip its audit trail.

### `BlockUnavailableError`

```
class BlockUnavailableError(RuntimeError)
```

The emergency-block mark could not be written / removed (cache outage).

### `ConsumerDefinitionError`

```
class ConsumerDefinitionError(DeclarationError)(
    message: str,
    *,
    fix: str | None = None,
) -> None
```

An invalid `@on` consumer (SANDLOOM-B016).

### `ContractArgumentError`

```
class ContractArgumentError(ContractCallError, TypeError)(
    detail: Any = None,
    *,
    status_code: int | None = None,
    headers: Mapping[str, str] | None = None,
    code: str | None = None,
) -> None
```

The call's arguments do not bind to / validate against the signature (422).

Raised BEFORE the provider runs, locally and remotely alike: unknown or missing arguments, positional-only / keyword-only violations, or a value that does not validate against the parameter's annotation.

### `ContractCallError`

```
class ContractCallError(HTTPException)(
    detail: Any = None,
    *,
    status_code: int | None = None,
    headers: Mapping[str, str] | None = None,
    code: str | None = None,
) -> None
```

A contract call failed with a declared status — identical locally and over RPC.

Every domain / protocol error a contract raises surfaces to the caller as this exception (an `HTTPException`): a provider `HTTPException` or an `http_status`-protocol exception is converted with the SAME status, JSON-encoded `detail`, `headers` (e.g. `Retry-After`) and `code` whether the provider is in-process or behind internal RPC. Framework errors keep their own subclass on both paths (`ContractIdempotencyConflict`, `ContractArgumentError`, …).

### `ContractCallInProgress`

```
class ContractCallInProgress(ContractCallError, RuntimeError)(
    detail: Any = None,
    *,
    status_code: int | None = None,
    headers: Mapping[str, str] | None = None,
    code: str | None = None,
) -> None
```

A command with this idempotency key is still running (503, retry later).

### `ContractDefinitionError`

```
class ContractDefinitionError(DeclarationError)(
    message: str,
    *,
    fix: str | None = None,
) -> None
```

An invalid `@contract` class or `@command` method (SANDLOOM-B018).

### `ContractIdempotencyConflict`

```
class ContractIdempotencyConflict(ContractCallError, RuntimeError)(
    detail: Any = None,
    *,
    status_code: int | None = None,
    headers: Mapping[str, str] | None = None,
    code: str | None = None,
) -> None
```

A command's idempotency key was reused with DIFFERENT arguments (409).

### `ContractIdempotencyKeyMissing`

```
class ContractIdempotencyKeyMissing(ContractCallError, ValueError)(
    detail: Any = None,
    *,
    status_code: int | None = None,
    headers: Mapping[str, str] | None = None,
    code: str | None = None,
) -> None
```

A command contract was called without its idempotency/operation key.

### `ContractIdempotencyUnavailable`

```
class ContractIdempotencyUnavailable(ContractCallError, RuntimeError)(
    detail: Any = None,
    *,
    status_code: int | None = None,
    headers: Mapping[str, str] | None = None,
    code: str | None = None,
) -> None
```

The command dedupe store is unreachable: the command is refused (503).

Commands fail CLOSED — running one without dedupe could duplicate its side effect on a caller retry.

### `ContractResultNotReplayable`

```
class ContractResultNotReplayable(ContractCallError, RuntimeError)(
    detail: Any = None,
    *,
    status_code: int | None = None,
    headers: Mapping[str, str] | None = None,
    code: str | None = None,
) -> None
```

A retried command already completed but its result was too large to keep (409).

The effect happened exactly once; only the result body is unavailable.

### `DeclarationError`

```
class DeclarationError(TypeError)(message: str, *, fix: str | None = None) -> None
```

Base class of the L1 declaration refusals.

### `EventDefinitionError`

```
class EventDefinitionError(DeclarationError)(
    message: str,
    *,
    fix: str | None = None,
) -> None
```

An invalid `Event` class, or `.publish()` on a view (SANDLOOM-B015; a view's `.publish()` is SANDLOOM-C025 statically).

### `EventMetaUnavailable`

```
class EventMetaUnavailable(AttributeError)
```

`event.meta` read on an event that was not delivered.

### `MissingTenantError`

```
class MissingTenantError(RuntimeError)
```

A tenant-scoped operation ran with no tenant bound in the context.

### `ModuleDiscoveryError`

```
class ModuleDiscoveryError(DeclarationError)(
    message: str,
    *,
    fix: str | None = None,
) -> None
```

Discovery refused the module: a declaration in the wrong place, two declarations claiming one name (SANDLOOM-B006).

### `ModuleRegistrationConflict`

```
class ModuleRegistrationConflict(RuntimeError)
```

A buffered registration would conflict with live kernel state.

Raised by `ModuleRegistrationDraft.preflight` BEFORE anything is applied, so a conflicting module leaves the kernel completely untouched.

### `ProjectionDefinitionError`

```
class ProjectionDefinitionError(DeclarationError)(
    message: str,
    *,
    fix: str | None = None,
) -> None
```

An invalid `Projection` (SANDLOOM-B012).

### `ReadContractWriteError`

```
class ReadContractWriteError(RuntimeError)
```

A read contract tried to write (its UoW is always rolled back).

Raised for published events, queued `on_commit` / `before_commit` effects, `record_audit` rows and ORM / DML writes — which a rolled-back read would otherwise drop silently while returning success. Declare the method `@contract_method(command=True, idempotency_key=...)`.

### `RemoteServiceUnavailable`

```
class RemoteServiceUnavailable(HTTPException)(
    service_name: str,
    *,
    reason: str,
    code: str = 'remote_service_unavailable',
) -> None
```

A remote service binding exists, but the provider cannot serve the call.

Transport / trust failures only (unreachable, timeout, a request or response that does not authenticate, provider crash) — never a domain error, which reaches the caller as the provider's own status. For `reason="timeout"` the remote call MAY HAVE COMMITTED: retrying a decorated command with the same idempotency argument replays its result instead of re-executing it. `code` names the failure for programmatic handling.

### `RoutePolicyError`

```
class RoutePolicyError(DeclarationError)(
    message: str,
    *,
    fix: str | None = None,
) -> None
```

A route is both `public()` and authenticated (SANDLOOM-B008).

### `SagaDefinitionError`

```
class SagaDefinitionError(ValueError)
```

A saga definition is malformed or does not match its contracts.

Raised when the definition is built (structure) and at startup (the called contracts); the message names the saga, the step and a `fix:`.

### `ServiceNotFound`

```
class ServiceNotFound(ServiceRegistryError)
```

Base error for service registry.

### `ServiceUnavailableInDeployment`

```
class ServiceUnavailableInDeployment(HTTPException, ServiceNotFound)(
    service_name: str,
    *,
    reason: str | None = None,
) -> None
```

A contract is neither provided locally nor bound to a remote provider.

Raised by `CoreFacade.get_service` so the configurable monolith degrades *explicitly* (501 Not Implemented) instead of a bare 500 when, in this deployment topology, nobody implements the contract. Subclasses `ServiceNotFound` so existing `except ServiceNotFound` handlers and the RPC server's not-found mapping keep working unchanged.

Lives here (next to `ServiceNotFound`) rather than in `sandloom.core.rpc` so the facade can raise it WITHOUT importing the concrete RPC mechanism; the RPC package re-exports it as part of its public API.

### `TaskEnqueueConflict`

```
class TaskEnqueueConflict(HTTPException)() -> None
```

An HTTP exception you can raise in your own code to show errors to the client.

This is for client errors, invalid authentication, invalid data, etc. Not for server errors in your code.

Read more about it in the [FastAPI docs for Handling Errors](<https://fastapi.tiangolo.com/tutorial/handling-errors/>).

## Example

\`\`python from fastapi import FastAPI, HTTPException

app = FastAPI()

items = {"foo": "The Foo Wrestlers"}

@app.get("/items/{item\_id}") async def read\_item(item\_id: str): if item\_id not in items: raise HTTPException(status\_code=404, detail="Item not found") return {"item": items\[item\_id\]} \`\`

### `TaskEnqueueUnavailable`

```
class TaskEnqueueUnavailable(HTTPException)() -> None
```

An HTTP exception you can raise in your own code to show errors to the client.

This is for client errors, invalid authentication, invalid data, etc. Not for server errors in your code.

Read more about it in the [FastAPI docs for Handling Errors](<https://fastapi.tiangolo.com/tutorial/handling-errors/>).

## Example

\`\`python from fastapi import FastAPI, HTTPException

app = FastAPI()

items = {"foo": "The Foo Wrestlers"}

@app.get("/items/{item\_id}") async def read\_item(item\_id: str): if item\_id not in items: raise HTTPException(status\_code=404, detail="Item not found") return {"item": items\[item\_id\]} \`\`

### `TaskEnqueueUncertain`

```
class TaskEnqueueUncertain(HTTPException)(task_id: str | None = None) -> None
```

An HTTP exception you can raise in your own code to show errors to the client.

This is for client errors, invalid authentication, invalid data, etc. Not for server errors in your code.

Read more about it in the [FastAPI docs for Handling Errors](<https://fastapi.tiangolo.com/tutorial/handling-errors/>).

## Example

\`\`python from fastapi import FastAPI, HTTPException

app = FastAPI()

items = {"foo": "The Foo Wrestlers"}

@app.get("/items/{item\_id}") async def read\_item(item\_id: str): if item\_id not in items: raise HTTPException(status\_code=404, detail="Item not found") return {"item": items\[item\_id\]} \`\`

### `TenantMismatchError`

```
class TenantMismatchError(RuntimeError)
```

A tenant-owned object does not belong to the bound tenant.

### `UnitOfWorkRequiredError`

```
class UnitOfWorkRequiredError(RuntimeError)
```

A guaranteed write-path effect was requested outside a Unit of Work.

Raised by `publish_event` when no UoW scope is active and the caller did not opt into best-effort semantics (`record_audit` raises its own `AuditUnavailableError` in the same situation). Without a UoW there is no transaction for the outbox row to commit with, so the guarantee ("published iff the change commits, never lost") cannot hold.

### `UnsafeTenantOperationError`

```
class UnsafeTenantOperationError(RuntimeError)
```

An ORM operation could bypass or mutate tenant ownership.

## Examples and related guides

- [Error catalogue](<https://sandloom.dev/docs/reference/error-codes/index.md>)
- [Python API overview](<https://sandloom.dev/docs/reference/sdk/index.md>)
