skip to Main Content

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.

Screenshot

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


  1. 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

    Login or Signup to reply.
  2. 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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search