pradip subedi
pradip subedi

Reputation: 11

Next.js error TypeError: Cannot read properties of undefined (reading 'headers')

I am working with Next.js 13.4.19 and I want to try OpenAI API. I have my API routes in good structure. When I hit the API route http://localhost:3000/api/course/createChapters for POST with:

{
    'title':'calculus',
    'units':['algebra','differentiation','integration']
}

I get the following error:

- error TypeError: Cannot read properties of undefined (reading 'headers')
    at eval (webpack-internal:///(rsc)/./node_modules/.pnpm/next@13.4.19_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/server/future/route-modules/app-route/module.js:266:61)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

Here follows my gpt.ts file:

import OpenAI from "openai";

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

interface OutputFormat {
  [key: string]: string | string[] | OutputFormat;
}

export async function strict_output(
  system_prompt: string,
  user_prompt: string | string[],
  output_format: OutputFormat,
  default_category: string = "",
  output_value_only: boolean = false,
  model: string = "gpt-3.5-turbo",
  temperature: number = 1,
  num_tries: number = 3,
  verbose: boolean = false
) {
  const list_input: boolean = Array.isArray(user_prompt);
  const dynamic_elements: boolean = /<.*?>/.test(JSON.stringify(output_format));
  const list_output: boolean = /\[.*?\]/.test(JSON.stringify(output_format));
  let error_msg: string = "";

  for (let i = 0; i < num_tries; i++) {
    let output_format_prompt: string = `\nYou are to output ${
      list_output && "an array of objects in"
    } the following in json format: ${JSON.stringify(
      output_format
    )}. \nDo not put quotation marks or escape character \\ in the output fields.`;

    if (list_output) {
      output_format_prompt += `\nIf output field is a list, classify output into the best element of the list.`;
    }
    if (dynamic_elements) {
      output_format_prompt += `\nAny text enclosed by < and > indicates you must generate content to replace it. Example input: Go to <location>, Example output: Go to the garden\nAny output key containing < and > indicates you must generate the key name to replace it. Example input: {'<location>': 'description of location'}, Example output: {school: a place for education}`;
    }

    if (list_input) {
      output_format_prompt += `\nGenerate an array of json, one json for each input element.`;
    }
    const response = await openai.chat.completions.create({
      temperature: temperature,
      model: model,
      messages: [
        {
          role: "system",
          content: system_prompt + output_format_prompt + error_msg,
        },
        { role: "user", content: user_prompt.toString() },
      ],
    });

    let res: string =
      response.choices[0].message?.content?.replace(/'/g, '"') ?? "";
    res = res.replace(/(\w)"(\w)/g, "$1'$2");

    if (verbose) {
      console.log(
        "System prompt:",
        system_prompt + output_format_prompt + error_msg
      );
      console.log("\nUser prompt:", user_prompt);
      console.log("\nGPT response:", res);
    }

    try {
      let output: any = JSON.parse(res);

      if (list_input) {
        if (!Array.isArray(output)) {
          throw new Error("Output format not in an array of json");
        }
      } else {
        output = [output];
      }

      for (let index = 0; index < output.length; index++) {
        for (const key in output_format) {
          if (/<.*?>/.test(key)) {
            continue;
          }
          if (!(key in output[index])) {
            throw new Error(`${key} not in json output`);
          }

          if (Array.isArray(output_format[key])) {
            const choices = output_format[key] as string[];
            if (Array.isArray(output[index][key])) {
              output[index][key] = output[index][key][0];
            }
            if (!choices.includes(output[index][key]) && default_category) {
              output[index][key] = default_category;
            }
            if (output[index][key].includes(":")) {
              output[index][key] = output[index][key].split(":")[0];
            }
          }
        }

        if (output_value_only) {
          output[index] = Object.values(output[index]);
          if (output[index].length === 1) {
            output[index] = output[index][0];
          }
        }
      }

      return list_input ? output : output[0];
    } catch (e) {
      error_msg = `\n\nResult: ${res}\n\nError message: ${e}`;
      console.log("An exception occurred:", e);
      console.log("Current invalid json format ", res);
    }
  }

  return [];
}

Here is my route.ts file

import { strict_output } from "@/lib/gpt";
import { createChaptersSchema } from "@/validators/course";
import { NextResponse } from "next/server";
import { ZodError } from "zod";

export const POST = async (req: Request) => {
  try {
    const body = await req.json();
    const { title, units } = createChaptersSchema.parse(body);

    type outptUnits = {
      title: string;
      chapters: {
        youtube_search_query: string;
        chapter_title: string;
      }[];
    };

    let output_units: outptUnits = await strict_output(
      "You are an AI capable of curating course content, coming up with relevant chapter titles, and finding relevant youtube videos for each chapter",
      new Array(units.length).fill(
        `It is your job to create a course about ${title}. The user has requested to create chapters for each of the units. Then, for each chapter, provide a detailed youtube search query that can be used to find an informative educationalvideo for each chapter. Each query should give an educational informative course in youtube.`
      ),
      {
        title: "title of the unit",
        chapters:
          "an array of chapters, each chapter should have a youtube_search_query and a chapter_title key in the JSON object",
      }
    );
    console.log(output_units);
    return NextResponse.json(output_units);
  } catch (error) {
    if (error instanceof ZodError) {
      return new NextResponse("Invalid Body", { status: 400 });
    }
  }
};

Schema file for prisma

model Course {
  id            String    @id @default(cuid())
  name String
  image String
  units Unit[]
}

model Unit {
  id            String    @id @default(cuid())
  name String
  chapters Chapter[]
  courseId String
  course Course @relation(fields: [courseId], references: [id])

  @@index([courseId], name:"courseId")
}

model Chapter {
  id            String    @id @default(cuid())
  unitId String
  name String
  youtubeSearchQuery String
  videoId String?
  summary String? @db.VarChar(3000)
  unit Unit @relation(fields: [unitId], references: [id])
  questions Question[]
  @@index([unitId], name:"unitId")
}

model Question {
  id            String    @id @default(cuid())
  chapterId String
  questionId String
  chapter Chapter @relation(fields: [chapterId], references: [id])
  question String @db.VarChar(3000)
  options String @db.VarChar(3000)
  answer String @db.VarChar(3000)

  @@index([chapterId], name:"chapterId")
}

My folder structure is as follows: Folder Structure

My Insomnia response looks like this: enter image description here

I tried to hit the API with Insomnia REST but I got no response. I was expecting the following to happen: enter image description here

Upvotes: 1

Views: 5675

Answers (4)

mohammed aslawy
mohammed aslawy

Reputation: 1

i have the same problem in my next.js ecommerce web app , solve that with install next-auth beta version

Upvotes: 0

Mohammed Ziyad
Mohammed Ziyad

Reputation: 61

I also faced the same issue. The problem is that the chat completion request to OpenAI was not going through due to error status code 429 "Too many requests".

I had 0 credits in my OpenAI account, so the API was not letting the request through. I created a new account and got $5 free credits. Generated an API Key using this account and used it in the app.

Was able to solve this issue and the API request got through and got a successful response.

Hope it helps!

Upvotes: 1

Anav Gupta
Anav Gupta

Reputation: 1

Check if your route.ts is in course/createChapters directory rather than the course/ directory, a similar error happened to me recently when my api page was under the wrong folder

Upvotes: 0

Hamza Kyamanywa
Hamza Kyamanywa

Reputation: 588

This error happens when your route handler does not return a response.

I am not sure but I suspect that there is an error thrown in your route-handler code that ends up in the catch block.

} catch (error) {
  if (error instanceof ZodError) {
    return new NextResponse("Invalid Body", { status: 400 });
  }
  //TODO: Handle other error cases

}

From looking at your catch block, it is only returning a response when the error is an instance of instance of ZodError.

Try to return a 500 or some other HTTP response in your catch block for other kinds of errors. I recommend logging that error in your dev environment to see it.

  } catch (error) {
    if (error instanceof ZodError) {
       return new NextResponse("Invalid Body", { status: 400 });
    }
    return new NextResponse("Internal server error", { status: 500 });
  }

Upvotes: 0

Related Questions