Skip to main content

ResultError

The library normalizes unknown values to a ResultError with:
  • code: stable identifier
  • message: error description
  • status?: optional numeric status
  • meta?: additional payload
  • cause?: original value

Default normalizer

If no custom rules are provided, trybox automatically applies the following rules:
  • abort: Detects AbortError and returns code: "ABORTED".
  • timeout: Detects TimeoutError and returns code: "TIMEOUT".
  • httpStatus: Detects errors with numeric status or statusCode. Returns code: "HTTP" and preserves the status.
  • aggregate: Handles AggregateError by saving internal errors in meta.errors.
  • string: If a string is thrown, it is used as the message.
  • message: Extracts the message property from objects.
Example using default rules:
import trybox from "trybox";

const runner = trybox(); // Uses default rules

const result = await runner.run(async () => {
  throw new DOMException("Time out", "TimeoutError");
});

if (!result.ok && result.error.code === "TIMEOUT") {
  console.log("Request timed out");
}

Custom rules

Use errorRule to define specific mappings and trybox to apply them.
import trybox, { errorRule } from "trybox";

class HttpError extends Error {
  constructor(
    public status: number,
    public url: string,
    public body?: unknown
  ) {
    super("HTTP error");
  }
}

const runner = trybox({
  rules: [
    errorRule.instance(HttpError).toError((e) => ({
      code: "HTTP",
      message: `Request failed (${e.status})`,
      status: e.status,
      meta: { url: e.url, body: e.body },
      cause: e,
    })),
  ],
});

Transformation with mapError

Allows adjusting the already normalized error to enrich or standardize it.
const r = await run(() => fetch("/api"), {
  mapError: (e) => ({ ...e, message: `[api] ${e.message}` }),
});

Cancellation and Timeout

  • signal and timeout affect how long we wait for the task to complete.
  • If your function does not consume the provided AbortSignal, the library cannot cancel underlying I/O automatically.
  • We race the task and return ABORTED/TIMEOUT promptly, but the inner request only stops if it cooperates with signal.
const controller = new AbortController();
const r = await run(() => fetch("/api"), { signal: controller.signal, timeout: 2000 });

Circuit Breaker (Runner-level)

  • Circuit breaker is configured at the Runner level: trybox({ circuitBreaker }).
  • Per-call circuit breaker is intentionally not exposed in RunOptions to keep the API lean.