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.
| Kind | Returned or thrown | How to detect | Retry? |
|---|---|---|---|
| Documented HTTP error | Returned | error is set; response.status has the code | No |
| Response doesn't match the API description | Thrown | Caught: failure instanceof SefariaContractError | Usually no. Report it |
| Request failed or was cancelled | Thrown | Any other error, often a TypeError or an AbortError | Network 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
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 }.erroris the documented body, already checked against the description. For the texts API it is{ error: string }.response.statushas the status code.- If you pass
throwOnError: true, the call throws that checked body instead.
Contract error, thrown.
SefariaContractErrormeans 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, andstatuspath, the path template, such as/api/v3/texts/{tref}issuesresponse, the originalResponse, if you need headers
Anything else, thrown. Usually the request failed (network, DNS, offline), or you cancelled it through an
AbortSignalpassed assignal. A mistake in your own call, such as an invalidbaseUrl, 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 asinvalid_type,content-type, orundocumented-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