@arki/failure for agents
For @arki/[email protected] · source docs/agent-guide.md
The long form of llms.txt. When they disagree, src/index.ts wins.
The model
- A failure is a frozen object
{ code, message, data, status, remediation }with a hidden brand. A function returns it like any other value. defineFailure(code, options)makes a definition. Calling the definition makes a failure.- Success has no wrapper. The return type is
Value | Failure<...>, inferred. - The data schema is any Standard Schema; the foundation imports spec types directly and never depends on
@arki/schema. - Misuse of the package throws
FailureError. That is a bug in the caller, not a failure. - The core has no runtime built-ins. It runs in Node, Bun, Deno, browsers and Hermes.
Task map
Define and return a failure
- Call
defineFailure('feature.reason', { status, data, message })once, in a module the whole feature can import. - Return
Def(data)where the thing is missing or refused. - Do not annotate the return type.
import { z } from 'zod';
import { defineFailure, isFailure, match, recover } from '@arki/failure';
export const PostNotFound = defineFailure('post.not_found', {
status: 404,
data: z.object({ id: z.string() }),
message: 'no post {id}',
});
type Post = { id: string; title: string };
const posts = new Map<string, Post>([['p1', { id: 'p1', title: 'Hello' }]]);
export const find = async (id: string) => posts.get(id) ?? PostNotFound({ id });Check a result
- Call
isFailure(result)and return it, or usematchto handle each code. - Use
recoverto handle some codes and pass the rest on.
export async function titleOf(id: string) {
const post = await find(id);
if (isFailure(post)) return post; // the failure joins this function's inferred result
return post.title; // narrowed to Post
}Handle every code
export async function label(id: string) {
return match(await find(id), {
ok: post => post.title,
'post.not_found': failure => `missing ${failure.data.id}`,
});
}Handle some codes
export async function titleOrDraft(id: string) {
return recover(await find(id), { 'post.not_found': () => 'Untitled draft' });
}Cross an oRPC boundary
- In the contract, add
.errors(errorMap(Def)). - In the procedure, return
orThrowRpc(usecaseResult, errors). - On the client, call
reviveRpcError(error, [Def])in acatch, rethrow unrelated errors (undefined), and handle the domain and canonicalREVIVAL_FAILURESbranches.
Turn a throwing call into a failure
- Wrap the call in
attempt(run, onThrow). - Return a failure from
onThrow, or rethrow what you do not recognise.
Lint discarded failures
- Add
failure.configs.recommendedfrom@arki/failure/eslinttoeslint.config.js. - Make sure TypeScript files use
typescript-eslintwithprojectService: true.
Rules that bite
- A discarded call drops its failure. Turn on
failure/must-use. - Truthiness does not narrow.
if (post)andpost ?? fallbackkeep the failure. unknown,anyandobjectswallow failures from a return type.- Wire data must be lossless JSON, accepted unchanged by its schema.
revivereturns typed protocol failures for invalid envelopes. - A returned failure does not roll back a transaction. Throw inside it.
401means "not signed in" only. Use422when an upstream refuses us.
Errors
| Code | Meaning | What to do |
|---|---|---|
ARKI_FAILURE_E001 | defineFailure got an empty code or ok. | Use a dotted code such as post.not_found. |
ARKI_FAILURE_E002 | Status is not an integer from 400 to 599. | Pass a valid status or none. |
ARKI_FAILURE_E003 | match met a code it has no handler for. | Add the handler; fix the cast that hid it. |
ARKI_FAILURE_E004 | unreachable was reached. | Handle the new case; validate data at the boundary. |
ARKI_FAILURE_E005 | errorMap got two definitions with one code. | Give each definition its own code. |
ARKI_FAILURE_E006 | Retired; use InvalidEnvelope. | Check stored data; avoid transforming schemas. |
ARKI_FAILURE_E007 | Retired; use AsyncSchema. | Use a synchronous schema for failure data. |
ARKI_FAILURE_E008 | An invariant condition was falsy. | Fix the caller that broke the assumption. |
Verify
bun run typecheck
bun run test
bun run test:docsSee typed validation migration for canonical failures, parsing and versioned JSON transport.