I have an issue that only appears on the production environment. It uploads an image on uploadthing
, but then does not redirect to the next page.
Error:
UploadThingError: Something went wrong. Please report this to UploadThing.
at O (323-ac80e073b694958b.js:1:134496)
at 323-ac80e073b694958b.js:1:39164Caused by: RetryError
at 323-ac80e073b694958b.js:1:36171
at 323-ac80e073b694958b.js:2:4679
at 323-ac80e073b694958b.js:2:3847
at 323-ac80e073b694958b.js:2:2259
at Object.s (323-ac80e073b694958b.js:2:2184)
at 323-ac80e073b694958b.js:2:3860
at 323-ac80e073b694958b.js:2:5316
at 323-ac80e073b694958b.js:2:6013
at 323-ac80e073b694958b.js:2:3670
at 323-ac80e073b694958b.js:2:2259
These are the source files:
src/app/api/uploadthing/core.ts
import { createUploadthing, type FileRouter } from "uploadthing/next";
import { z } from "zod";
import sharp from "sharp";
import { db } from "@/db";
const f = createUploadthing();
export const ourFileRouter = {
imageUploader: f({ image: { maxFileSize: "4MB" } })
.input(z.object({ configId: z.string().optional() }))
.middleware(async ({ input }) => {
return { input };
})
.onUploadComplete(async ({ metadata, file }) => {
const { configId } = metadata.input;
const res = await fetch(file.url);
const buffer = await res.arrayBuffer();
const imgMetadata = await sharp(buffer).metadata();
const { width, height } = imgMetadata;
if (!configId) {
const configuration = await db.configuration.create({
data: {
imageUrl: file.url,
height: height || 500,
width: width || 500,
},
});
return { configId: configuration.id };
} else {
const updatedConfiguration = await db.configuration.update({
where: {
id: configId,
},
data: {
croppedImageUrl: file.url,
},
});
return { configId: updatedConfiguration.id };
}
}),
} satisfies FileRouter;
export type OurFileRouter = typeof ourFileRouter;
src/app/configure/upload/page.tsx
"use client";
import { Progress } from "@/components/ui/progress";
import { useToast } from "@/hooks/use-toast";
import { useUploadThing } from "@/lib/uploadthing";
import { cn } from "@/lib/utils";
import { Image, Loader2, MousePointerSquareDashed } from "lucide-react";
import { useRouter } from "next/navigation";
import { useState, useTransition } from "react";
import Dropzone, { FileRejection } from "react-dropzone";
const Page = () => {
const { toast } = useToast();
const [isDragOver, setIsDragOver] = useState<boolean>(false);
const [uploadProgress, setUploadProgress] = useState<number>(0);
const router = useRouter();
const { startUpload, isUploading } = useUploadThing("imageUploader", {
onClientUploadComplete: ([data]) => {
console.log(data);
const configId = data.serverData.configId;
startTransition(() => {
router.push(`/configure/design?id=${configId}`);
});
},
onUploadProgress(p) {
setUploadProgress(p);
},
onUploadError: (err) => {
console.log(err);
},
});
const onDropRejected = (rejectedFiles: FileRejection[]) => {
const [file] = rejectedFiles;
setIsDragOver(false);
toast({
title: `${file.file.type} type is not supported.`,
description: "Please choose a PNG, JPG, or JPEG image instead.",
variant: "destructive",
});
};
const onDropAccepted = (acceptedFiles: File[]) => {
try {
startUpload(acceptedFiles, { configId: undefined });
setIsDragOver(false);
} catch (error) {
console.log(error);
}
};
const [isPending, startTransition] = useTransition();
return (
<div
className={cn(
"relative h-full flex-1 my-16 w-full rounded-xl bg-gray-900/5 p-2 ring-1 ring-inset ring-gray-900/10 lg:rounded-2xl flex justify-center flex-col items-center",
{
"ring-blue-900/25 bg-blue-900/10": isDragOver,
}
)}
>
<div className="relative flex flex-1 flex-col items-center justify-center w-full">
<Dropzone
onDropRejected={onDropRejected}
onDropAccepted={onDropAccepted}
accept={{
"image/png": [".png"],
"image/jpeg": [".jpeg"],
"image/jpg": [".jpg"],
}}
onDragEnter={() => setIsDragOver(true)}
onDragLeave={() => setIsDragOver(false)}
disabled={isUploading || isPending}
>
{({ getRootProps, getInputProps }) => (
<div
className="h-full w-full flex-1 flex flex-col items-center justify-center"
{...getRootProps()}
>
<input {...getInputProps()} />
{isDragOver ? (
<MousePointerSquareDashed className="h-6 w-6 text-zinc-500 mb-2" />
) : isUploading || isPending ? (
<Loader2 className="animate-spin h-6 w-6 text-zinc-500 mb-2" />
) : (
<Image className="h-6 w-6 text-zinc-500 mb-2" />
)}
<div className="flex flex-col justify-center mb-2 text-sm text-zinc-700">
{isUploading ? (
<div className="flex flex-col items-center">
<p>Uploading...</p>
<Progress
value={uploadProgress}
className="mt-2 w-40 h-2 bg-gray-300"
/>
</div>
) : isPending ? (
<div className="flex flex-col items-center">
<p>Redirecting, please wait...</p>
</div>
) : isDragOver ? (
<p>
<span className="font-semibold">Drop file</span> to upload
</p>
) : (
<p>
<span className="font-semibold">Click to upload</span> or
drag and drop
</p>
)}
</div>
{isPending ? null : (
<p className="text-xs text-zinc-500">PNG, JPG, JPEG</p>
)}
</div>
)}
</Dropzone>
</div>
</div>
);
};
export default Page;
These are the dependencies:
"@uploadthing/react": "^6.7.2",
"axios": "^1.7.7",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"framer-motion": "^11.5.4",
"lucide-react": "^0.439.0",
"next": "14.2.8",
"prisma": "^5.19.1",
"react": "^18",
"react-dom": "^18",
"react-dom-confetti": "^0.2.0",
"react-dropzone": "^14.2.3",
"react-rnd": "^10.4.12",
"resend": "^4.0.0",
"sharp": "^0.33.5",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7",
"uploadthing": "^6.13.2",
"zod": "^3.23.8"
I already tried several things like changing the version of uploadthing
and @uploadthing/react
, but to no avail.
2
Answers
If I understood the problem correctly, as the docs say
only assets that are in the public directory at
build time
are rendered by Next.js. Added files will not be available at the time of request. We recommend using a third-party service such as Vercel Blob for permanent file storage.https://nextjs.org/docs/pages/building-your-application/optimizing/static-assets
To fix this you will need to use The Protection Bypass for Automation feature provided by vercel by generating a secret and adding this secret to your uploadThing settings. Follow the guide here: https://docs.uploadthing.com/faq.
You can read more about this feature on vercel here: https://vercel.com/docs/security/deployment-protection/methods-to-bypass-deployment-protection/protection-bypass-automation