Error handling
Review how to handle errors (exceptions) generated by both your own Durable Object code as well as exceptions thrown by Durable Objects’ infrastructure (such as overloads or network errors).
How exceptions are thrown
There are three ways in which a Durable Object can throw an exception:
- From the code within the
fetch()
handler defined on your Durable Object class. The resulting exception will have a.remote
property set toTrue
in this case. - From code within the
constructor
method defined on your Durable Object class. The resulting exception will have a.remote
property set toTrue
in this case. - Generated by Durable Object’s infrastructure due to transient internal errors, or because you are sending too many requests to a single Durable Object, or when too many requests are queued due to slow or excessive I/O (external API calls or storage operations) within an individual Durable Object.
Some of the exceptions generated by Durable Object’s infrastructure will also have the
.remote
property set toTrue
.
Refer to Troubleshooting to review the types of errors returned by a Durable Object and/or Durable Objects infrastructure and how to prevent them.
Understanding stubs
A Durable Object stub is a client Object used to send requests to a remote Durable Object. To learn more about how to make requests to a Durable Object, refer to Create Durable Objects stubs and Access a Durable Objects from a Worker.
Example
Any uncaught exceptions thrown by your code within a Durable Object’s fetch()
handler or constructor
method will be propagated to the caller’s fetch()
call. Catching these exceptions allows you to retry creating the Durable Object stub and sending the request:
export interface Env { ErrorThrowingObject: DurableObjectNamespace;
}
async function makeRequest(env: Env, id: DurableObjectId) { // Create the Durable Object stub here, because certain types of errors will break // the Durable Object stub, so you need to create it here to re-create the stub when // this function is called in your retry logic. const doStub = env.ErrorThrowingObject.get(id); const resp = await doStub.fetch("http://your-do/");
return Response.json(resp);
}
export default { async fetch( req, env, ctx ): Promise<Response> { let userId = new URL(req.url).searchParams.get("userId") || ""; const id = env.ErrorThrowingObject.idFromName(userId); // Try sending the request try { return await makeRequest(env, id); } catch (e: any) { // Retry logic for sending the request. // It is recommended you retry requests to your Durable Object as long // as the operation in your Durable Object is idempotent. try { return await makeRequest(env, id); } catch (e: any) { // Retry still did not work, therefore return an error. return new Response("server error", { status: 500 }); } } },
} satisfies ExportedHandler<Env>;
export class ErrorThrowingObject implements DurableObject { constructor(state: DurableObjectState, env: Env) { // Any exceptions that are raised in your constructor will also set the // .remote property to True throw "no good"; }
async fetch(req: Request) { // Generate an uncaught exception // A .remote property will be added to the exception propagated to the caller // and will be set to True throw new Error("example error");
// We never reach this return Response.json({}); }
}