Errors Crossing RPC boundaries?
I burned a number of hours today before i discovered surprising behavior when an Error subtype is thrown and then caught by an RPC caller. Here is the doc ...
https://developers.cloudflare.com/workers/runtime-apis/rpc/error-handling/
Example:
If Worker1 calls Worker2 (via RPC), and Worker2 throws FooError extends Error { constructor(public bar:string = "testing") }
... then Worker1 receives the FooError serialized to Error, essentially all Exceptions are converted to Error (with all specialized attributes stripped). The error.message is mostly retained, but that's it?
I have a whole family of exception that have specialized metadata that is all being stripped out?
Does anyone have any strategy/workaround suggestions to deal with this?
Maybe i need to trash exceptions and rely on "Error" return types to be passed across RPC boundaries? Thoughts?
Maybe i need to trash exceptions and rely on "Error" return types to be passed across RPC boundaries? Thoughts?
2 Replies
Instead of relying on implicit serialization, serialize your specialized exceptions explicitly into a structured format (e.g., JSON) before passing them across the boundary.
You can try:
In worker1:
async function callWorker2() {
const response = await fetch("https://worker2.example.com");
if (!response.ok) {
const errorDetails = await response.json();
if (errorDetails.type === "FooError") {
console.error("Caught a FooError:", errorDetails.bar);
} else {
console.error("Caught an error:", errorDetails.message);
}
}
}
In Worker2:
class FooError extends Error {
constructor(public bar: string = "testing") {
super(
FooError: ${bar});
this.name = "FooError";
}
}
async function handleRequest(request) {
try {
throw new FooError("This is a custom error");
} catch (error) {
if (error instanceof FooError) {
return new Response(JSON.stringify({
type: error.name,
message: error.message,
bar: error.bar
}), { status: 500 });
}
return new Response("Internal Server Error", { status: 500 });
}
}
Thanks @Beginner
Yes, i'm not as concerned about fetch(), that would clearly require and API that communicated serialized error info (rest, or graphql,...).
Instead i'm referring to private RPC between Workers and DO's (direct function calls).
RPC stubs are take care of the full serialization of input params and return values. But caught Error subtypes (Ex: ValidationError, NotFoundError, etc...) that contain extra error metadata are all being stripped to plain Error objects (losing additional error info)?
For now, i am only allowing technical errors to be thrown, and anything i think that might need to be handled or recovered from by a client i will model as a formal return type.
Ex:
CreateUser(input): User | UserInputError | AgeConstraintError ...
CreateUser(input): User | UserInputError | AgeConstraintError ...