Exceptions
Reference for Solwyn SDK exceptions — BudgetExceededError, ProviderUnavailableError, ConfigurationError, and the translation errors
import os
from openai import OpenAI
from solwyn import BudgetExceededError, ConfigurationError, ProviderUnavailableError, Solwyn
try:
client = Solwyn(
OpenAI(),
api_key=os.environ["SOLWYN_API_KEY"],
budget_mode="hard_deny",
)
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello!"}],
)
except ConfigurationError as e:
print(f"Bad config: {e.message} (field: {e.field})")
except BudgetExceededError as e:
print(f"Over budget: ${e.current_usage:.2f} / ${e.budget_limit:.2f}")
except ProviderUnavailableError as e:
print(f"Provider down: {e.provider}")Every exception below is importable from the top-level solwyn package. UnsupportedSurfaceError joined them in SDK v0.3.0; on older SDKs it needs the deep import from solwyn.exceptions import UnsupportedSurfaceError.
BudgetExceededError
Raised when a request would exceed the configured budget limit. Only raised in budget_mode="hard_deny" mode. In "alert_only" mode, the SDK logs a warning instead of raising.
When raised
- Before an LLM call, when the pre-flight budget check returns "denied" and
budget_mode="hard_deny". - New in 0.3.0: the same check also enforces per-run budget caps — a call denied against the cap on its agent run raises this same exception, with the same attributes.
Attributes
| Attribute | Type | Description |
|---|---|---|
project_id | str | None | Project identifier resolved by the API, if available. |
budget_limit | float | Configured spending cap in dollars. |
current_usage | float | Amount already consumed in the current period. |
estimated_cost | float | Estimated cost of the blocked request. |
budget_period | str | Budget window (e.g. "daily", "weekly", "monthly", or "unknown" if not provided by the API). |
mode | str | Active budget mode when the error was raised. |
Example
import os
from openai import OpenAI
from solwyn import BudgetExceededError, Solwyn
client = Solwyn(
OpenAI(),
api_key=os.environ["SOLWYN_API_KEY"],
budget_mode="hard_deny",
)
try:
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Summarize this document."}],
)
except BudgetExceededError as e:
print(f"Budget exhausted: ${e.current_usage:.4f} used of ${e.budget_limit:.2f} cap")
print(f"Blocked request estimated cost: ${e.estimated_cost:.4f}")
print(f"Period: {e.budget_period}")
print(f"Mode: {e.mode}")
client.close()ProviderUnavailableError
Raised when every provider in the failover chain is unavailable — all circuits open, or the chain is exhausted without a success.
When raised
- When a provider's circuit breaker is
OPEN, the router skips it. If no candidate in the chain can serve the call, the SDK raises this error rather than sending a request that is bound to fail.
Attributes
| Attribute | Type | Description |
|---|---|---|
provider | str | None | The unavailable provider (e.g. "openai"), or None when the error describes an exhausted chain rather than a single provider. |
circuit_state | str | None | Current circuit breaker state ("closed", "open", "half_open"), or None. |
attempted | list[str] | None | Ordered list of providers the router tried before giving up. Never contains prompt content. |
Example
import os
from openai import OpenAI
from solwyn import ProviderUnavailableError, Solwyn
client = Solwyn(
OpenAI(),
api_key=os.environ["SOLWYN_API_KEY"],
)
try:
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello!"}],
)
except ProviderUnavailableError as e:
print(f"Provider {e.provider} is unavailable (circuit: {e.circuit_state})")
print(f"Chain attempted: {e.attempted}")
client.close()UntranslatableRequestError
Raised when a cross-provider failover hop cannot translate a request from the source provider's dialect into the target's. Raised before any network call, aborting the whole candidate chain. Carries only structural labels describing what could not be translated — never the offending value and never prompt content.
When raised
- During a cross-provider hop, when a request feature has no equivalent on the target provider (e.g. an OpenAI
response_formatAnthropic cannot express, a dangling tool call, or a parameter outside the target's accepted range). - On hops involving a Bedrock entry, with fixed structural labels such as
bedrock.guardrail_config(a safety feature a hop would otherwise silently strip),cache_control(BedrockcachePointblocks),missing_max_tokens(inferenceConfig.maxTokensis required for a cross-provider hop — the SDK never invents an output bound),image.opaque_handle(s3Locationimage sources), andunsupported_kwarg.<key>for any unrecognized keyword. - For cross-dialect tool-using streams (e.g. an OpenAI-dialect tool stream failing over to Anthropic) — raised pre-dispatch, before any network call. Same-dialect hops between OpenAI-compatible providers are native passthrough and never raise this.
Attributes
| Attribute | Type | Description |
|---|---|---|
source | str | Provider the request was authored against (e.g. "openai"). |
target | str | Provider the request was being translated toward (e.g. "anthropic"). |
feature | str | Structural token naming the untranslatable shape (e.g. "response_format", "dangling_tool_call"). Never the offending value or prompt content. |
Example
import os
from anthropic import Anthropic
from openai import OpenAI
from solwyn import Solwyn, UntranslatableRequestError
client = Solwyn(
OpenAI(),
model="gpt-4o",
api_key=os.environ["SOLWYN_API_KEY"],
fallback=[(Anthropic(), "claude-sonnet-4-5", {"max_tokens": 1024})],
)
try:
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello!"}],
)
except UntranslatableRequestError as e:
print(f"Cannot translate {e.feature} from {e.source} to {e.target}")
client.close()UntranslatableModelError
Raised when a fallback entry has no concrete model configured for its target provider. A model id is a configuration value (not prompt content), so it is safe to surface in the message.
When raised
- During a cross-provider hop, when the target entry's
modelis empty or missing — caught structurally before any network call so the chain aborts like every other translation error.
Attributes
| Attribute | Type | Description |
|---|---|---|
model | str | The model identifier that has no mapping. |
provider | str | The target provider the model was not configured for. |
UnsupportedSurfaceError
Raised when a provider adapter does not serve a requested media surface. New in 0.2.0, alongside the non-text modality surfaces.
Non-chat media surfaces (embeddings, images, audio, video) dispatch through a per-adapter seam; an adapter with no branch for the requested surface raises this instead of letting a spend surface run untracked. Both attributes are configuration values — a surface name and a provider name — never prompt content.
When raised
videos.createon an OpenAI-compatible or native Together client — Solwyn's video interception is wired for OpenAI's Sora only. See Surface coverage.- Any media surface on a client whose adapter serves no seam for it —
embeddings.createon a wrapped Anthropic client, for instance.
Attributes
| Attribute | Type | Description |
|---|---|---|
surface | str | The media surface that was requested (e.g. "embeddings"). |
provider | str | The provider whose adapter does not serve it. |
Import
UnsupportedSurfaceError is exported from the top-level package (SDK v0.3.0+):
from solwyn import UnsupportedSurfaceErrorOn SDKs before 0.3.0, use the deep import — it still works on current versions:
from solwyn.exceptions import UnsupportedSurfaceErrorConfigurationError
Raised when SDK configuration is invalid or incomplete — almost always at construction time, with one deliberate call-time case (the Bedrock invoke_model guard).
When raised
- When
Solwyn()orAsyncSolwyn()is constructed with an invalidapi_keyformat, a malformedfallbackspec (field="fallback_specs"), or an unknown config field. - At construction, when a
provider=override (constructor argument or 4th fallback-tuple element) names an unknown provider (the message lists the known values), targets a client object that is not a recognized provider SDK client, or mismatches the client's API dialect — an override can only relabel a client within its own dialect (field="provider"). See OpenAI-compatible providers. - At call time, when
invoke_modelorinvoke_model_with_response_streamis called on a Bedrock-wrapped client (field="invoke_model"). This is a budget-bypass guard: those surfaces fail loud instead of letting calls run untracked. Useconverse()/converse_stream(), or the unwrapped boto3 client for deliberately untracked calls. See Amazon Bedrock.
Attributes
| Attribute | Type | Description |
|---|---|---|
field | str | None | Configuration field that failed validation. May be None for general errors. |
message | str | Human-readable description of the problem. |
Example
from openai import OpenAI
from solwyn import ConfigurationError, Solwyn
try:
client = Solwyn(
OpenAI(),
api_key="bad_key_format",
)
except ConfigurationError as e:
print(f"Configuration error: {e.message}")
if e.field:
print(f"Invalid field: {e.field}")Exception hierarchy
SolwynError
+-- BudgetExceededError
+-- ProviderUnavailableError
+-- ConfigurationError
+-- UntranslatableRequestError
+-- UntranslatableModelError
+-- UnsupportedSurfaceErrorSolwynError is the base class for all Solwyn SDK exceptions. Catch it to handle any error originating from the SDK without distinguishing the specific cause:
from solwyn import SolwynError
try:
response = client.chat.completions.create(...)
except SolwynError as e:
# catches every Solwyn-raised error
...Import
Every exception class is exported from the top-level package:
from solwyn import (
BudgetExceededError,
ConfigurationError,
ProviderUnavailableError,
SolwynError,
UnsupportedSurfaceError,
UntranslatableModelError,
UntranslatableRequestError,
)UnsupportedSurfaceError was added to the root export in SDK v0.3.0. On earlier versions, import it from solwyn.exceptions instead.