SOLWYN

TypeScript SDK Errors

Catch typed budget, run-control, provider, configuration, coverage, and failover errors from the TypeScript SDK.

Every public Solwyn SDK error extends the built-in Error through SolwynError. Catch concrete classes when your application has a recovery action, and use SolwynError as the final SDK-wide boundary. Provider SDK errors remain provider errors.

Catch errors with instanceof

TypeScript catch values should be treated as unknown. Narrow by class rather than parsing an error message:

import {
  BudgetExceededError,
  ProviderUnavailableError,
  RunStoppedError,
  Solwyn,
  SolwynError,
} from "@solwyn/sdk";
import OpenAI from "openai";

const solwynApiKey = process.env.SOLWYN_API_KEY;
if (!solwynApiKey) throw new Error("SOLWYN_API_KEY is required");

const client = new Solwyn(new OpenAI(), { apiKey: solwynApiKey });

try {
  await client.chat.completions.create({
    model: "gpt-4o",
    messages: [{ role: "user", content: "Process the next work item." }],
  });
} catch (error: unknown) {
  if (error instanceof RunStoppedError) {
    console.error(`Run ${error.agentRunId} stopped by ${error.source}: ${error.reason}`);
  } else if (error instanceof BudgetExceededError) {
    console.error(`Budget ${error.currentUsage} / ${error.budgetLimit}`);
  } else if (error instanceof ProviderUnavailableError) {
    console.error("No provider could be dispatched", error.attempted);
  } else if (error instanceof SolwynError) {
    console.error(error.name, error.message);
  } else {
    throw error; // provider error or application error
  }
} finally {
  await client.close();
}

RunStoppedError is deliberately not a BudgetExceededError; both are direct concrete branches under SolwynError. Keep the run-stop handler separate so a generic budget retry cannot swallow an operator or velocity stop.

Error hierarchy

Error
└─ SolwynError
   ├─ BudgetExceededError
   ├─ RunStoppedError
   ├─ ProviderUnavailableError
   ├─ ConfigurationError
   ├─ UnsupportedSurfaceError
   ├─ UntrackedSpendSurfaceError
   ├─ CoverageMismatchError
   ├─ SurfaceInspectionError
   ├─ UntranslatableRequestError
   └─ UntranslatableModelError

The SDK can also throw SolwynError directly for a small number of invalid runtime invariants:

  • malformed per-call or run tag values, or invalid run handles and names;
  • an intercepted call made after close() begins, which rejects with "client is closed";
  • a LangChain or OpenAI Agents path that the attribution recipe does not admit. These refusals fail closed, and LangChain refusals are marked non-retryable for LangChain.

A final instanceof SolwynError branch covers those cases.

Concrete error reference

BudgetExceededError

Thrown before provider dispatch when an ordinary budget decision blocks the call:

  • a live Cloud hard denial;
  • an applicable hard denial retained through a Cloud outage; or
  • local fail-closed enforcement when failOpen: false, including the no-prior-limit case.
FieldTypeMeaning
projectIdstring | nullCloud project ID, or null when the outage path has no project snapshot.
budgetLimitnumberLimit attached to the decision; 0 when unknown.
currentUsagenumberUsage attached to the decision; 0 when unknown.
estimatedCostnumberDiagnostic $0.00003 × estimated input tokens, not provider pricing.
budgetPeriodstringCloud denied_by_period, or "unknown" for a local decision without one.
modestringMode attached to the live or local decision.

See budgets for live-mode authority, retained denials, and outage order.

RunStoppedError

Thrown when the active exact run ID was stopped by a Cloud directive or deny-eligible local velocity rule. It blocks later dispatches and aborts an active stream at the next raw provider-chunk boundary, regardless of failOpen or budgetMode.

FieldTypeMeaning
agentRunIdstringExact stopped run ID.
reasonstringBounded structural stop reason.
source"server" | "local_velocity"Where the stop originated.

See runs and streaming.

ProviderUnavailableError

Thrown when no candidate can be dispatched—for example, every applicable circuit is open, candidate selection returns no usable target, or the failover window expires before another hop may start.

FieldTypeMeaning
providerstring | nullProvider on the legacy provider/state construction shape.
circuitStatestring | nullCircuit state on the legacy construction shape.
attemptedstring[] | nullOrdered provider-name list when the router can supply one.

If provider attempts actually execute and the chain is exhausted, the final provider's original exception is re-thrown instead of being replaced with ProviderUnavailableError. See failover.

ConfigurationError

Thrown synchronously or before dispatch when the SDK cannot safely honor a configuration or request. Its field: string | null identifies the rejected option or structural field when available. Constructor and environment schema errors use the internal snake_case name, such as budget_check_timeout.

Common cases include:

  • an absent or malformed Solwyn project key;
  • an unknown constructor option, a non-function fetch, a partial logger, or a selectionPolicy without order();
  • a provider pin that conflicts with the provider client's family;
  • wrapping an already wrapped client;
  • a malformed fallback tuple, tag map, timeout, or coverage acknowledgment;
  • a request-options body/query override that could differ from the request admitted by the budget check;
  • a blocked Bedrock InvokeModel* command (including InvokeModelWithBidirectionalStreamCommand), StartAsyncInvokeCommand, or a callback-style intercepted send call;
  • unsupported Responses shapes such as background creation or streaming responses.parse;
  • coverage(...) called on an object that is not a Solwyn wrapper (field: "client");
  • LangChain or OpenAI Agents attribution without @solwyn/sdk/node loaded (field: null), a raw provider client passed instead of a Solwyn wrapper (field: "client"), an invalid LangChain namePrefix, or an invalid Agents api or OpenAIProvider option.
import { ConfigurationError, Solwyn } from "@solwyn/sdk";
import OpenAI from "openai";

try {
  new Solwyn(new OpenAI({ apiKey: "test-provider-key" }), {
    apiKey: "not-a-project-key",
  });
} catch (error: unknown) {
  if (!(error instanceof ConfigurationError)) throw error;
  console.error("Invalid field:", error.field);
}

UnsupportedSurfaceError

Thrown when the selected known provider adapter cannot safely dispatch a tracked surface. Fields: surface: string and provider: string. One example is reaching a tracked embeddings resource on a dialect whose adapter cannot serve embeddings.

UntrackedSpendSurfaceError

Thrown before provider I/O when an unknown or explicitly unmetered spend surface is reached with onUnmetered: "raise".

FieldType
surfacestring
tokenstring | null
providerstring
clientShapeProvider surface-shape label
kindEffective surface kind
capabilityScopeCapability-scope label or null
driftedFromRuleIdReviewed rule ID or null

Since 0.2.0, legacy raw-request and client escapes on native OpenAI, Azure OpenAI, Anthropic, and Together clients, OpenAI Realtime session and client-secret creation and call acceptance, and Google GenAI Live and ApiClient transports report kind: "unmetered_spend" with explicit capability scopes instead of "unknown".

Use providers for the reviewed surface boundary and configuration for onUnmetered and acknowledgeUntracked.

CoverageMismatchError

Thrown by coverage(client).expect(...) when the observed provider graph differs from a pinned expectation. differences is a non-empty readonly string[] describing the structural drift.

SurfaceInspectionError

Thrown when coverage(...) cannot safely inspect part of a provider client. It carries path: string, a closed structural stage label, and causeType: string; it does not retain the original thrown value or its message.

UntranslatableRequestError

Thrown before target-provider I/O when a cross-dialect failover request falls outside the minimal translation subset. It carries only source, target, and feature structural labels. Translation errors abort the whole fallback chain.

UntranslatableModelError

Thrown before target-provider I/O when a cross-dialect fallback does not have a usable target model. It carries model and provider. See failover.

Provider errors pass through

Solwyn classifies provider failures to decide breaker health and whether another hop is safe, but it does not replace request-shaped or terminal provider errors. OpenAI RateLimitError, Anthropic APIError, AWS service exceptions, Google errors, and other provider values can therefore reach your existing handlers unchanged.

If you configure fallbacks, a failure may be consumed when policy permits another attempt. If the router must stop, it rethrows the original provider value; treat non-Solwyn catches as unknown and use the provider SDK's own type guards.

Control-plane and reporting failures

Not every operational failure becomes an exception on the model-call path:

  • Budget-check outages follow failOpen, last-known-limit, lease, and retained-authority rules.
  • Metadata and settlement delivery is queued and retried; a reporting outage does not replace a successful provider response.
  • Per-event ingest rejections are logged rather than thrown from an already completed LLM call. Ordinary rejected events are terminal; eligible denial receipts are folded into bounded aggregates and can be replayed after recovery.
  • Unmetered surfaces warn once by default; choose onUnmetered: "raise" to make them fail closed.

Inject a logger to route those diagnostics into your application's logging system. Use offline testing to exercise denial, outage, stop, and ingest-rejection paths without real network traffic, and privacy for the structural-only error and diagnostic boundary.

On this page