Documentation: 0.5.0 (source: v0.5.0).
# Errors

> The typed error model, and what each error actually tells you.

Markdown source of https://docs-v0-5-0.atlas-kit-docs.pages.dev/concepts/errors/ — see https://docs-v0-5-0.atlas-kit-docs.pages.dev/ai/ for the full machine-readable surface.

Every failure the SDK raises extends `SageSdkError` and carries a `code`. That
is deliberate: matching on a code is stable, while matching on a message string
breaks the first time someone improves the wording.

The `instanceof` check is not optional. In TypeScript a caught value is
`unknown`, so reading `error.code` without narrowing first does not compile —
and the narrowing is what tells you the error came from the SDK rather than
from your own code or the network stack.

```ts
import { SageSdkError } from '@aephia/atlas-kit/client';

try {
  await sage.characters.forProfile(profileAddress);
} catch (error) {
  if (error instanceof SageSdkError && error.code === 'ACCOUNT_NOT_FOUND') {
    // Handle the expected case.
  }
}
```

## The codes worth knowing

**`ACCOUNT_NOT_FOUND`** — nothing exists at that address on this network.
Either the address is wrong, or the RPC is pointed at a different network. This
is the most common error, and the second cause is the one people miss.

**`INVALID_ACCOUNT_OWNER`** — something exists there, but it belongs to a
different program. The address is for something else entirely.

**`INVALID_DISCRIMINATOR`** — the account exists and belongs to SAGE, but it is
a different account type than this read expected.

**`INVALID_ENTITY_ID`** — the value is not a valid address.

**`MISSING_GAME_CONTEXT`** — a non-preset cluster was used without supplying a
Game address.

**`RELATIONSHIP_NOT_DISCOVERABLE`** — the relationship has no derivable address
and no discovery provider was configured. See [identity](/guides/identity/) for
the common case.

**`PROVIDER_ERROR`** — the RPC endpoint itself failed. Often rate limiting.

**`RESOURCE_LIMIT_EXCEEDED`** — a response exceeded a configured safety bound.

## Absence is not an error

Many reads have a `maybe*` form that returns `undefined` instead of throwing:

```ts
const standing = await maybeGetFactionStanding(ctx, profileAddress, factionId);
```

The distinction is intent. Use `get*` when absence would be a bug in your
application, and `maybe*` when absence is an ordinary outcome. A profile with
no faction standing is not an error; a fleet address that resolves to nothing
probably is.

## Invalid data never reaches the cache

Validation happens before decoding: program owner, discriminator, data shape,
minimum length. Data that fails becomes a typed error and is never cached as
valid state.

This is worth internalising, because the alternative is worse than it sounds.
Without it, a truncated or mistaken RPC response becomes a plausible-looking
value deep inside your application, and the bug surfaces somewhere unrelated,
much later.

## Reference

- [`client`](/reference/client/) — the error types
- [The bindings escape hatch](/concepts/bindings/) — where these guarantees stop
