Skip to content

Created/edited by GitHub Copilot; pending human review.

Handle errors in your code ​

This page has not been reviewed by a human.

A generated call such as text.getV3Texts({ client, path: { tref } }) either returns a result or throws. Three different failures need three different responses.

KindReturned or thrownHow to detectRetry?
Documented HTTP errorReturnederror is set; response.status has the codeNo
Response doesn't match the API descriptionThrownCaught: failure instanceof SefariaContractErrorUsually no. Report it
Request failed or was cancelledThrownAny other error, often a TypeError or an AbortErrorNetwork failure: maybe. Cancellation: no

This page covers errors in your own code. If a component on a visible page shows an error, see Troubleshoot a page.

Branch on each kind ​

examples/site-snippets/client-errors.ts
import {
  SefariaContractError,
  createSefariaClient,
  text,
} from "@sefaria/api-client";
import type { SefariaClient } from "@sefaria/api-client";

async function describePassage(
  client: SefariaClient,
  tref: string,
  signal?: AbortSignal,
) {
  try {
    const { data, error, response } = await text.getV3Texts({
      client,
      path: { tref },
      ...(signal === undefined ? {} : { signal }),
    });
    if (data === undefined) {
      // 1. Sefaria answered with an error it documents, such as 404.
      return `Sefaria error ${response.status}: ${error?.error}`;
    }
    return `${data.ref}: ${data.versions.length} edition(s)`;
  } catch (failure) {
    if (failure instanceof SefariaContractError) {
      // 2. The response didn't match the API description.
      const report = {
        operationId: failure.operationId,
        method: failure.method,
        path: failure.path,
        status: failure.status,
        issues: failure.issues.map((issue) => ({
          instancePath: issue.instancePath,
          keyword: issue.keyword,
          message: issue.message,
        })),
      };
      return `Unexpected response shape: ${JSON.stringify(report)}`;
    }
    // 3. The request failed or was cancelled. There is no data to show.
    return `Request failed: ${String(failure)}`;
  }
}

// Stand-in servers make each case happen on demand. Use createSefariaClient()
// with no options to talk to Sefaria.
const json = (body: unknown, status = 200) =>
  new Response(JSON.stringify(body), {
    status,
    headers: { "content-type": "application/json" },
  });

const notFound = createSefariaClient({
  fetch: async () => json({ error: "Could not find title in reference" }, 404),
});
const htmlPage = createSefariaClient({
  fetch: async () =>
    new Response("<html>Service unavailable</html>", {
      headers: { "content-type": "text/html" },
    }),
});
const offline = createSefariaClient({
  fetch: async () => {
    throw new TypeError("fetch failed");
  },
});

console.log(await describePassage(notFound, "Micah 6:80"));
console.log(await describePassage(htmlPage, "Micah 6:8"));
console.log(await describePassage(offline, "Micah 6:8"));
console.log(
  await describePassage(
    createSefariaClient(),
    "Micah 6:8",
    AbortSignal.abort(new DOMException("Reader left the page", "AbortError")),
  ),
);

After you press Run, you see four lines. The first reports a Sefaria 404 error. The second shows the details of a contract error. The last two report a network failure and a cancellation.

The demo passes stand-in fetch functions to createSefariaClient({ fetch }). Each case then happens on demand, without the network. In real code, use createSefariaClient().

  • Documented error, returned. Sefaria may answer with a status that its API description documents as an error (for the texts API, 400 and 404). Then you get { data: undefined, error, response }.

    • error is the documented body, already checked against the description. For the texts API it is { error: string }.
    • response.status has the status code.
    • If you pass throwOnError: true, the call throws that checked body instead.
  • Contract error, thrown. SefariaContractError means the response doesn't match the API description. Causes include:

    • a wrong body shape
    • invalid JSON
    • a content type the description doesn't list (such as an HTML error page)
    • a status the description doesn't document for that operation, such as an unexpected 500

    The error has these fields:

    • operationId, method, and status
    • path, the path template, such as /api/v3/texts/{tref}
    • issues
    • response, the original Response, if you need headers
  • Anything else, thrown. Usually the request failed (network, DNS, offline), or you cancelled it through an AbortSignal passed as signal. A mistake in your own call, such as an invalid baseUrl, also lands here. You get the original error or abort reason. The client never turns these into empty or success-shaped data. Treat a cancellation as expected, not as a fault.

Read a validation path ​

Each entry in issues has these fields:

  • instancePath: a JSON Pointer to the place in the response that doesn't match. An empty string means the whole response, as in the contract error the demo prints.
  • schemaPath: which response schema in the API description was used. It isn't the exact rule that failed.
  • keyword: the kind of mismatch, such as invalid_type, content-type, or undocumented-status.
  • message: a readable explanation. It is optional in the type, but the client fills it in.

The path shows where the mismatch is. It doesn't say how the description should change.

Should you retry? ​

The client doesn't retry anything automatically, so the decision is yours.

  • A network failure may be worth retrying with backoff.
  • A cancellation isn't: your code asked for it.
  • A documented 404, such as an unknown reference, is unlikely to change if you simply retry.
  • A contract error is also unlikely to go away by itself. Report it.

The client keeps a small in-memory cache per client. It stores only successful, checked responses, so errors are never cached.

What to report ​

When you ask for help, include operationId, method, path, status, and the issues (instancePath, keyword, message). The snippet's report object has exactly these fields. Leave out personal data. See Get support.

Learn more: The client and Sefaria's API · Client reference

Sefaria Frontend ToolkitCommunity-driven with Sefaria backing and support. GitHub Get support

AI-generated documentation.

About this documentation

Like the codebase, this documentation was scaffolded and written with GitHub Copilot. I don't have a background in technical writing, and wanted to get something out there.

Pages that have not undergone any human review are explicitly marked, but the others may also read funny or need work too. Sorry about that.

Please report any issues so they can be prioritized.

Thanks, Avi