---
title: Declared errors
description: Give expected failures stable data, HTTP behavior, and retry guidance without exposing internal defects.
sidebar:
  icon: circle-alert
---

Declare an error when callers need to handle an expected failure, such as an order that does not exist. An unexpected exception is a defect: RELKIT records it internally and returns a safe boundary failure
instead of exposing a stack or provider detail.

## Scaffold a typed error

From the application root, with an existing `orders` service:

```sh
bun run relkit add error receipt-rejected --service orders
```

Creates `src/services/orders/errors/receipt-rejected.error.ts` with `{ message: string }` data and
`retry: "never"`. It stays internal. Add it to the relevant function's `errors` list yourself;
a service bundle containing an error wires it to the bundle's example function automatically.

For every scaffolding flag, see the [error command reference](/docs/api/cli#relkit-add-error-name).

## Define a stable failure

```ts title="examples/commerce/src/services/orders/errors/order-not-found.error.ts"
/** Carries the missing order identity from lookup to an HTTP 404 with the established one-second retry hint. */
import { defineError } from "@relkit/app/functions";
import { z } from "@relkit/app/schema";

const orderNotFound = defineError({
  id: "orders.not-found",
  data: z.object({
    /** Requested order identity preserved in the public failure message and payload. */
    orderId: z.string(),
  }),
  message: ({ orderId }) => `Order ${orderId} was not found`,
  http: { status: 404 },
  retry: { kind: "later", afterMs: 1_000 },
});

export default orderNotFound;
```

The `id` is the durable contract used by graphs, clients, traces, and matching. `data` limits the details callers
may receive. `http.status` contributes a typed HTTP response, while `retry` tells durable delivery whether another
attempt could help.

Register the descriptor in a function's `errors` list and throw the constructed declared value. Do not convert
every caught exception into a declared error; callers should depend only on failures the domain promises.

## Verify the public contract

The order lookup function uses the declared error when no record exists:

```ts title="examples/commerce/src/services/orders/functions/get-order.function.ts"
/** Emits lookup progress and returns the demonstration order or its declared order-not-found error. */
import { defineFunction } from "@relkit/app/functions";
import { z } from "@relkit/app/schema";
import orderNotFound from "../errors/order-not-found.error.js";
import { orderLookupInput, orderLookupOutput } from "../../../platform/schemas.js";

const getOrder = defineFunction({
  input: orderLookupInput,
  output: orderLookupOutput,
  progress: z.object({
    stage: z.union([z.literal("lookup-started"), z.literal("lookup-completed")]),
  }),
  errors: [orderNotFound],
  handler: async (input, context) => {
    await context.progress.emit({ stage: "lookup-started" });
    if (input.orderId === "missing") return new orderNotFound(input);
    await context.progress.emit({ stage: "lookup-completed" });
    return { orderId: input.orderId, status: "confirmed", totalCents: 1_000 };
  },
});

export default getOrder;
```

```sh title="Terminal — relkit-orders"
bun run check
curl -i http://localhost:3000/orders/missing
```

Expect HTTP `404` with the stable error code and validated data. Tests should assert those fields and the status,
not a complete internal message or stack trace.

## Treat retry as bounded guidance

- `"never"` means the same operation should not be retried automatically.
- `"later"` allows a durable transport to use its normal backoff.
- `{ kind: "later", afterMs }` supplies a minimum delay; the integration may wait longer.

HTTP and direct invocation do not automatically retry. Jobs and event delivery apply their own bounded policies,
so a retryable error never means an unbounded loop. See [task retries](/docs/modules/jobs/retries-and-idempotency) and
[Event delivery](/docs/modules/events/consume) for transport behavior.

{/* Generated by apps/docs/scripts/generate.ts. Do not edit. */}

**Reference and source**

- [`@relkit/functions` API](/docs/api/functions)

- [`examples/commerce/src/services/orders/errors/order-not-found.error.ts`](https://github.com/rel-kit/relkit/blob/main/examples/commerce/src/services/orders/errors/order-not-found.error.ts)
