Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected token < in JSON at position 0. Status code 500 in Nextjs 13 production build (App router) #633

Open
DongnutLa opened this issue Oct 8, 2023 · 9 comments

Comments

@DongnutLa
Copy link

Describe the bug in a sentence or two.

Unexpected token < in JSON at position 0. Status code 500 in Nextjs 13 production build (App router)

Issue Type (Can be multiple)

[ ] Build - Can’t install or import the SDK
[ ] Babel - Babel errors or cross browser issues
[ ] Performance - Performance issues
[X] Behaviour - Functions aren’t working as expected (Such as generate URL)
[ ] Documentation - Inconsistency between the docs and behaviour
[ ] Incorrect Types - For typescript users who are having problems with our d.ts files
[ ] Other (Specify)

Steps to reproduce

Create a route src/app/api/upload/route.ts and use the upload_stream method to upload any image

//src/app/api/upload/route.ts 

import { NextRequest, NextResponse } from "next/server";
import { UploadApiResponse, v2 as cloudinary } from "cloudinary";

const cloud_name = process.env.NEXT_CLOUDINARY_CLOUDNAME;
const api_key = process.env.NEXT_CLOUDINARY_API_KEY;
const api_secret = process.env.NEXT_CLOUDINARY_API_SECRET;

cloudinary.config({
  cloud_name,
  api_key,
  api_secret,
});

export async function POST(request: NextRequest) {
  const data = await request.formData();
  const image = data.get("file") as File;

  const bytes = await image.arrayBuffer();
  const buffer = Buffer.from(bytes);

  const response = await new Promise<UploadApiResponse>((resolve, reject) => {
    const stream = cloudinary.uploader.upload_stream({}, (err, res) => {
      if (err) reject(err);

      resolve(res!);
    });

    stream.write(buffer);
    stream.end();
  });

  return NextResponse.json({
    message: "File successfully uploaded",
    url: response.secure_url,
    id: response.public_id,
  });
}

Run next dev command and send a request to http://localhost:3000/api/upload with the image. This works fine

Now, run next build && next start and send same request to the same url and server will throw an HTTP 500 error:

Error screenshots

Screenshot 2023-10-07 at 7 56 47 PM

Browsers (if issue relates to UI, else ignore)

[ ] Chrome
[ ] Firefox
[ ] Safari
[ ] Other (Specify)
[ ] All

Versions and Libraries (fill in the version numbers)

Cloudinary_NPM SDK version 1.41.0
Node - 18.15.0
NPM - 9.6.4

Config Files (Please paste the following files if possible)

// package.json
{
  "name": "cloudinary-test",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "cloudinary": "^1.41.0",
    "next": "13.5.4",
    "react": "^18",
    "react-dom": "^18"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "typescript": "^5"
  }
}

Repository

https://github.com/DongnutLa/cloudinary-next13

@colbyfayock
Copy link

colbyfayock commented Oct 8, 2023

Hey @DongnutLa have you tried passing the buffer into the end method? I'm not terribly familiar with the stream API but it's worked for me before. Here's a svelte example https://github.com/cloudinary-community/cloudinary-examples/blob/main/examples/sveltekit-image-upload/src/routes/%2Bpage.server.js#L34

@DongnutLa
Copy link
Author

Hey @DongnutLa have you tried passing the buffer into the end method? I'm not terribly familiar with the stream API but it's worked for me before. Here's a svelte example https://github.com/cloudinary-community/cloudinary-examples/blob/main/examples/sveltekit-image-upload/src/routes/%2Bpage.server.js#L34

Yeah, I have already tried that, but I'm getting the same error

@wissam-khalili
Copy link

Hi @DongnutLa,

Perhaps you can also try something along the lines of:

let cloudinary = require("cloudinary").v2;
let streamifier = require('streamifier');

let uploadFromBuffer = (req) => {

   return new Promise((resolve, reject) => {

     let cld_upload_stream = cloudinary.v2.uploader.upload_stream(
      {
        folder: "foo"
      },
      (error: any, result: any) => {

        if (result) {
          resolve(result);
        } else {
          reject(error);
         }
       }
     );

     streamifier.createReadStream(req.file.buffer).pipe(cld_upload_stream);
   });

};

let result = await uploadFromBuffer(req);

You can find more examples here as well.

Hope you find it useful.
Regards,
Wissam

@colbyfayock
Copy link

@DongnutLa i wasn't quite able to get your repo moving "as is" since there was no UI to work from, however

  • I commented out the mimetype and filesize check
  • I updated const buffer ... to const buffer = new Uint8Array(arrayBuffer);
  • you don't have your console.error inside of the if ( err ) check so its going to show up regardless
  • i removed stream.write and instead am using stream.end(buffer)

and with that i was able to successfully post an image file using formdata from the client

see this PR i set on your reproduction repo: https://github.com/DongnutLa/cloudinary-next13/pull/1/files

@colbyfayock
Copy link

colbyfayock commented Oct 9, 2023

oh and important to note, i was never able to reproduce the error you were seeing for some reason...

however i was getting:

[UPLOAD ERROR] TypeError: Cannot read properties of null (reading 'split')

which i think is related to the mimetype code, hence me commenting it out

@DongnutLa
Copy link
Author

Hi @DongnutLa,

Perhaps you can also try something along the lines of:

let cloudinary = require("cloudinary").v2;
let streamifier = require('streamifier');

let uploadFromBuffer = (req) => {

   return new Promise((resolve, reject) => {

     let cld_upload_stream = cloudinary.v2.uploader.upload_stream(
      {
        folder: "foo"
      },
      (error: any, result: any) => {

        if (result) {
          resolve(result);
        } else {
          reject(error);
         }
       }
     );

     streamifier.createReadStream(req.file.buffer).pipe(cld_upload_stream);
   });

};

let result = await uploadFromBuffer(req);

You can find more examples here as well.

Hope you find it useful. Regards, Wissam

It works in development mode, but in a production build fails

@DongnutLa
Copy link
Author

@DongnutLa i wasn't quite able to get your repo moving "as is" since there was no UI to work from, however

  • I commented out the mimetype and filesize check
  • I updated const buffer ... to const buffer = new Uint8Array(arrayBuffer);
  • you don't have your console.error inside of the if ( err ) check so its going to show up regardless
  • i removed stream.write and instead am using stream.end(buffer)

and with that i was able to successfully post an image file using formdata from the client

see this PR i set on your reproduction repo: https://github.com/DongnutLa/cloudinary-next13/pull/1/files

I check your changes, but in production build it's still failing
Did you built the app and tried with next start ? Because in development mode it works well, but in production I'm getting the same 500 error.

@tamaracloudinary
Copy link
Contributor

@DongnutLa if that works in dev mode, try to search for differences between your production and dev environment setups.

@colbyfayock
Copy link

hey @DongnutLa i put together this example:

https://github.com/cloudinary-community/cloudinary-examples/tree/main/examples/nextjs-route-handlers-upload

i deployed it on Vercel and it worked as expected

because it didnt include the same options, I also created a separate test just to make sure

https://github.com/colbyfayock/test-nextjs-route-handlers-upload/blob/main/src/app/api/upload/route.ts#L16

i also had no problems with uploading through this mechanism

one thing i did hit was a similar error when trying to upload a file that was too big. this could come from a few different places

image

though notice the error response

serverless functions have a body size limit: https://vercel.com/guides/how-to-bypass-vercel-body-size-limit-serverless-functions

there are also file size limits depending on your plan from Cloudinary, but higher than that amount: https://support.cloudinary.com/hc/en-us/articles/202520592-Do-you-have-a-file-size-limit-#:~:text=On%20our%20free%20plan%2C%20the,also%20limited%20to%2010%20MB.

where are you deploying your application? the only other consideration is if you're using the vercel Edge runtime but i don't see any configuration in your example using such, as I believe the Node SDK wouldn't be supported there at this time

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants