---
title: "Errors"
description: "How each SDK separates a business verdict from a programming or infrastructure failure."
---

# Errors

How each SDK separates a business verdict from a programming or infrastructure
failure.

OpenPit draws one line through its whole surface:

- A **reject** is an expected business outcome. A policy looked at the request
  and said no. Rejects are always returned as values, never raised, and they
  carry a stable [reject code](Reject-Codes.md).
- An **error** is a failure of the call itself: invalid input, lifecycle misuse,
  an impossible configuration, or a boundary failure. Errors use whatever the
  language considers idiomatic - a returned error value, a raised exception, or
  a thrown object.

Handling code should branch on the first and let the second surface loudly. A
reject is normal traffic; an error means the integration is wrong or the
process is in trouble.

The reject record itself - its fields and their meaning - is described in
[Pre-trade Pipeline](Pre-trade-Pipeline.md#rejects), and the code vocabulary in
[Reject Codes](Reject-Codes.md).

## Go

Pipeline calls return three values: the handle, the rejects, and the error.

Policy rejects from the start stage, the main stage, and the evaluation
failures from drop copy come back as the second value, a reject slice. A
non-empty slice means the call produced no handle; an empty one means the stage
passed. Drop copy ignores ordinary policy verdicts and returns only the
failures that prevented historical bookkeeping.

Infrastructure failures and API misuse come back as the third value, an
ordinary Go error:

- from a pipeline or post-trade call, a transport-level or lifecycle failure,
  never a business reject;
- from a policy builder, an invalid configuration.

Business rejects use the stable code constants exported by the reject package,
for example the order-quantity-limit code when an order exceeds its configured
cap.

`RetireAccount` returns `*reject.AccountRetirementError` for a domain failure;
its `Kind` distinguishes refusal from finalizer failure, and `Refusals` lists
the refusing policies in registration order.

## Python

Rejects are returned on the result objects of the start stage, the main stage,
and the account-adjustment batch. The result object is falsy when the stage did
not pass, so a plain truth test is enough to branch.

Exceptions are reserved for invalid API usage and unexpected callback failures:
a type error for a wrong wrapper type, a value error for an invalid domain
value such as an empty asset, and a runtime error for lifecycle misuse such as
executing the same request twice. Admin account-blocking calls raise their own
dedicated exception. Constructing an `AccountBlock` raises `ValueError` for an
unrecognized code and `OverflowError` for user data outside `0..2**64-1`;
`block_with_cause` itself raises `OverflowError` only where the platform word
is narrower than 64 bits.

Custom policies must not raise for a normal risk decision - return a policy
reject instead.

The full Python exception surface, including the fields each exception carries,
is documented in the
[Python errors guide](https://openpit.readthedocs.io/en/stable/guides/errors.html).

## JavaScript and TypeScript

Policy rejects from the start stage and the main stage are not exceptions:
they are returned on the result object, as an `ok` flag plus a `rejects` array.

Malformed JavaScript shapes throw a native `TypeError`. Invalid values and
numeric ranges throw `RangeError` subclasses such as `ParamError`,
`AssetError`, and `AccountIdError`, all rooted at `OpenpitValueError`.
Lifecycle and engine-state failures use named `Error` subclasses.

Every error OpenPit constructs at the boundary is also branded, so
`instanceof OpenpitError` remains the catch-all in **both** the Node and the
browser build. Branch on the native category, the concrete class, or the stable
`err.name`:

<!-- markdownlint-disable-next-line MD013 -->
<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/bindings/js/tests/examples.readme.test.ts -->

```ts
import { ParamError, OpenpitError } from "@openpit/engine";
import { Price } from "@openpit/engine/param";

try {
  Price.fromString("not a number");
} catch (err) {
  if (err instanceof ParamError) {
    console.error(err.code); // e.g. "InvalidFormat"
  } else if (err instanceof OpenpitError) {
    console.error(err.name, err.message);
  }
}
```

The base class and every subclass are exported from the root entry point;
`AccountBlockError` is also re-exported from the reject subpath. The subclasses
are:

- `ParamError` - invalid numeric input, arithmetic overflow, or a malformed
  value.
- `AssetError` / `AccountIdError` - empty or invalid asset or account
  identifiers.
- `LifecycleError` - single-use misuse: executing the same request twice,
  finalizing the same reservation twice, using a stale account-control handle,
  reusing an engine builder that was already consumed, or re-entering an engine
  or one of its retained accounts or configuration facades from its own
  callback.
- `InternalError` - a defect inside the engine, reported instead of a bare
  WebAssembly trap. It poisons the loaded module instance: every later call that
  would reach core state returns this error instead of touching it, across the
  whole state-bearing surface. Discard every handle from that instance and
  reload the module.
- `EngineBuildError` - building an engine with no policy registered, or with a
  duplicate policy name or group id, or with an invalid built-in configuration.
- `PolicyConfigureError` - unknown policy, settings-type mismatch, or rejected
  update.
- `PolicyCallbackError` - a custom callback threw. Its `cause` is the original
  thrown value, whatever its class; `result` carries the completed post-trade or
  account-adjustment result when that operation produces one. An operation
  abandoned by an engine defect reports the internal error instead.
- `MarketDataError` (with `UnknownInstrument` and `QuoteUnavailable`),
  `RegistrationError` (with `AlreadyRegistered` and `UnknownInstrumentId`),
  `AccountGroupRegistrationError`, and `AccountBlockError` for the remaining
  market-data, registration, account-group, and admin-block failures.

Two stable code vocabularies appear on the JavaScript surface, and they do not
overlap:

- `err.code` is set only on `ParamError`, `AssetError`, and `AccountIdError`. It
  classifies a value failure - for example `"InvalidFormat"`, `"Overflow"`,
  `"Negative"`, or `"Other"` when no finer code applies. `ParamError.param`
  identifies the affected value type and `ParamError.input` preserves malformed
  text where the core reports it. The other subclasses leave `code` undefined.
- A `kind` field tells apart the variants of one failure family.
  `AccountBlockError.kind` is `"ReservedGroup"`, `"AccountNotBlocked"`, or
  `"GroupNotBlocked"`. Registration, account-group, engine-build, and
  policy-configuration failures expose their own stable `kind` plus structured
  fields such as the instrument id, account id, policy name, policy group id,
  and the expected and found values, so callers never parse a human-readable
  message.

The reject code vocabulary is separate again: it is carried on a business
reject and never on a thrown error.

## C++

Expected business outcomes are returned as values:

- pre-trade rejects from the start stage, the main stage, and the shortcut
  call;
- account operation outcomes from the account-control and adjustment APIs.

Programmer mistakes, invalid input, lifecycle misuse, and runtime boundary
failures throw `openpit::Error` or a more specific derived type such as
`openpit::EngineBuildError` for an impossible engine configuration or
`openpit::ConfigureError` for a rejected runtime reconfiguration. Catching the
base type is enough for a generic handler; the derived types carry the
structured detail.

## Rust

Both pipeline stages return a `Result` whose error side is the collected
rejects, so a reject is still a value rather than a panic: the start stage
yields a deferred request or the rejects, and the main stage yields a
reservation or the rejects.

Everything else is a dedicated error enum returned in a `Result`: engine
construction, runtime reconfiguration, value-type construction and checked
arithmetic, admin account blocking, account-group registration, and reference
book registration each have their own. Production code paths do not panic.

`Engine::retire_account` returns `AccountRetirementError::Refused` with policy
refusals, or `AccountRetirementError::FinalizerFailed` when a commit finalizer
fails.

## C

For account retirement, `openpit_engine_retire_account` reports domain failures
through `OpenPitAccountRetirementError`. Its kind is `Refused` or
`FinalizerFailed`; for a refusal, the error carries each policy name and
refusal kind. See [Account Retirement](Account-Retirement.md).

## Related Pages

- [Reject Codes](Reject-Codes.md): The stable business reject vocabulary and the
  evaluation-failure classifier
- [Pre-trade Pipeline](Pre-trade-Pipeline.md): The reject record and where each
  stage returns it
- [Account Blocking](Account-Blocking.md): The mutation finalizer contract, where a
  failure has no caller to report to
- [Policy API](Policy-API.md): How a custom policy reports a reject instead of
  failing the call
