Error Handling
Error Formatting
Added in
v5.2
You can customize the server-side behavior in your API handler's options by using an error formatter.
By default, the client only receives a generic message like
"Failed to run middleware"
to avoid leaking any sensitive information. You can
customize this behavior by specifying the errorFormatter
option when you
initialize your file route helper. An error formatter runs on the server and
takes the original UploadThingError
, and returns a JSON-serializable object.
The error also includes a cause
property which contains more information about
the nature of the error and what caused the error to throw in the first place.
You can also throw an UploadThingError
inside your middleware which will send
the error message to the client. All other error types will use a generic error
message. Regardless of what error is thrown, you can still change the defaults
with errorFormatter
.
import {
createUploadthing,
UploadThingError,
type FileRouter,
} from "uploadthing/server";
const f = createUploadthing();
const auth = (req: Request) => ({ id: "fakeId" });
export const ourFileRouter = {
imageUploader: f({ image: { maxFileSize: "4MB" } })
.middleware(async ({ req }) => {
const user = await auth(req);
if (!user) throw new Error(`Cant find user from req: ${req.toString()}`); // client onError will get "Failed to run middleware"
if (!user.id) throw new UploadThingError("No user ID"); // client onError will get "No user ID"
return { userId: user.id };
})
.onUploadComplete(async ({ metadata, file }) => {
console.log("Upload complete for userId:", metadata.userId);
console.log("file url", file.url);
return { uploadedBy: metadata.userId };
}),
} satisfies FileRouter;
export type OurFileRouter = typeof ourFileRouter;
UploadThingError
For most users, throwing an UploadThingError("your message")
will be enough.
For advanced use cases, you can pass an options object for more control.
type UploadThingErrorOptions<T> =
| {
/**
* ERROR_CODES:
* BAD_REQUEST: 400,
* NOT_FOUND: 404,
* FORBIDDEN: 403,
* INTERNAL_SERVER_ERROR: 500,
* INTERNAL_CLIENT_ERROR: 500,
* // S3 specific
* TOO_LARGE: 413,
* TOO_SMALL: 400,
* TOO_MANY_FILES: 400,
* KEY_TOO_LONG: 400,
* // UploadThing specific
* URL_GENERATION_FAILED: 500,
* UPLOAD_FAILED: 500,
* MISSING_ENV: 500,
* FILE_LIMIT_EXCEEDED: 500,
* @default `INTERNAL_SERVER_ERROR`
*/
code?: keyof typeof ERROR_CODES;
/**
* Your error message describing what happened
* @default `An unknown error occurred`
*/
message?: string;
/**
* The original error that caused this, if any.
*/
cause?: unkown;
/**
* Data associated with the error
*/
data?: T;
}
| string;
If you're using Zod as an input parser, you can return information of what
fields failed validation by checking if the cause is a ZodError
. Zod provides
a flatten
method that returns a JSON-serializable object which we can return
to the client.
import * as z from "zod";
import { createUploadthing } from "uploadthing/next";
import type { FileRouter } from "uploadthing/next";
const f = createUploadthing({
errorFormatter: (err) => {
return {
message: err.message,
zodError: err.cause instanceof z.ZodError ? err.cause.flatten() : null,
};
},
});
export const uploadRouter = {
withInput: f(["image"]).input(z.object({ foo: z.string() })),
// ...
} satisfies FileRouter;
Catching errors on the client
You can catch errors on the client by using the onUploadError
property on the
premade components, or the useUploadthing
hook. You can access the JSON object
that you returned from your error formatter on the data
property:
<UploadButton
endpoint="withInput"
input={{ foo: userInput }}
onUploadError={(error) => {
console.log("Error: ", error);
const fieldErrors = error.data?.zodError?.fieldErrors;
// ^? typeToFlattenedError
setError(fieldErrors.foo[0] ?? "");
}}
/>