Next.js Discord

Discord Forum

Headers cannot be modified

Answered
Clown posted this in #help-forum
Open in Discord
I am getting the following error when deploying my app to vercel. It seems to work just fine when building and running locally(npm start) so i assume this has to do with the route being deployed as a serverless function.

As you can see from the error i cant gather much info other than its happening in the /dashboard route (serverless function).

The only place i could find a next/headers usage inside the dashboard is inside my navbar placed inside my layout.tsx:

export default async function Navbar() {
  const newHeaders = new Headers(headers());
  newHeaders.set("Authorization", `BEARER ${process.env.INTERNAL_SECRET}`);
  var organization = await fetch(API_ORGANIZATION_PATH, {
    headers: newHeaders,
  });

  if (!organization.ok) {
    // TODO: better handle this when making custom error page.
    throw await organization.status;
  }
  var organizationJSON = (await organization.json()) as Organization;

  .... rest of the code

Lemme know if middleware code is needed, although from the logs i can see its executing without an error.
Answered by Ray
  fetch('', {
    headers: {
      ...Object.fromEntries(headers()),
      "Authorization", `BEARER ${process.env.INTERNAL_SECRET}`
    }
  })
View full answer

79 Replies

Laysan Albatross
yeah as far as I know, fetch is not the best solution for getting Server Side Props. Try using approx instead
axios* lmao
Whaaaa. Thats an issue?
Ah ok I'll give it a try.
I got many other routes to change if this works 😭
@Clown Whaaaa. Thats an issue?
Laysan Albatross
I mixed it up, sorry. It can lead to issues when fetching Server-Side Props with locally with Node.js fetch
Laysan Albatross
But from Server to Server it should be fine.
@Clown But what about that new header im setting :/
  fetch('', {
    headers: {
      ...Object.fromEntries(headers()),
      "Authorization", `BEARER ${process.env.INTERNAL_SECRET}`
    }
  })
Answer
Laysan Albatross
@Laysan Albatross yeah as far as I know, fetch is not the best solution for getting Server Side Props. Try using approx instead
I have never had issue with fetch in server side and I wouldn't recommend axios these day
Laysan Albatross
Because of security or performance?
Laysan Albatross
hahahah
lmao didnt know that xd
@Ray ts fetch('', { headers: { ...Object.fromEntries(headers()), "Authorization", `BEARER ${process.env.INTERNAL_SECRET}` } })
just to confirm, you meant this right?:
var organization = await fetch(API_ORGANIZATION_PATH, {
    headers: {
      ...Object.fromEntries(headers()),
      Authorization: `BEARER ${process.env.INTERNAL_SECRET}`,
    },
  });
Laysan Albatross
@Ray use let/const instead of var <:lolsob:753870958489632819>
Oh yeah lol. I have pushed it, let's see if it works. I got api routes using something similar too
@Ray yes
Nope getting the same error
I got the same thing in my middleware but i didnt change it there since its not throwing an error in it
@Clown Nope getting the same error
hmm it work for me
API_ORGANIZATION_PATH is the external api?
Its an API Route, i know, not the best thing to use but i gotta deal with it for now
ok let me try with an api route
also i found one more instance in /dashboard routeL
 const itemCountRes = await fetch(API_ITEM_COUNT_PATH, {
    headers: headers(),
  });
  const itemCount = await itemCountRes.json();
  const itemGroupCountRes = await fetch(API_ITEM_GROUP_COUNT_PATH, {
    headers: headers(),
  });
  const itemGroupCount = await itemGroupCountRes.json();
Surely its not giving me an error from some child route that i havent loaded yet :/
Maybe i should try commenting code out, its kinda annoying since i can only test on vercel itself and the deployment takes a bit of time
Hmm.... Ok
Its next 14
Wait I'll try give you a more specific version
how does your middleware look like
let me try it
dont mind the trash code 😛
import {
  withMiddlewareAuthRequired,
} from "@auth0/nextjs-auth0/edge";
import { NextResponse } from "next/server";
import { API_ORGANIZATION_PATH, BASE_URL, DASHBOARD_BASE_PATH } from "./app/api/api_constants";

export default withMiddlewareAuthRequired(async function middleware(req) {

  const newHeaders = new Headers(req.headers);
  newHeaders.set("Authorization", `BEARER ${process.env.INTERNAL_SECRET}`);
  var organization = await fetch(API_ORGANIZATION_PATH, {
    headers: newHeaders,
  });

  if (organization.ok) {
    if (req.nextUrl.pathname.endsWith("/dashboard/organization/create")) {
      return NextResponse.redirect(DASHBOARD_BASE_PATH);
    }
  }
  else {
    switch (organization.status) {
      case 401:
        return NextResponse.json({ error: "Unauthorized", }, { status: 401 });
      case 404:
        if (!req.nextUrl.pathname.endsWith("/dashboard/organization/create")) {
          return NextResponse.redirect(
            `${BASE_URL}/dashboard/organization/create/`,
          );
        }
        break;
      // default:
      //   return NextResponse.json({ error: "Server Error", }, { status: 500 });
    }
  }

});

export const config = { matcher: ["/dashboard/:path*"] };
maybe its an api route causing this :/
@Clown maybe its an api route causing this :/
could you show the code on api route too
Organization api route just has this line which uses headers:
const secret = verifyParseBearer(req.headers.get("Authorization"));

Another place is item count routes which has this abomination, this part wasnt by me iirc:

 const newHeaders = new Headers(req.headers);
    newHeaders.set("Authorization", `BEARER ${process.env.INTERNAL_SECRET}`);
    newHeaders.delete("content-length");
    var organization = await fetch(API_ORGANIZATION_PATH, {
        headers: newHeaders,
    });
    const organizationJson = await organization.json();
o [Error]: Headers cannot be modified. Read more: https://nextjs.org/docs/app/api-reference/functions/headers
    at Proxy.callable (/var/task/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:111608)
    at T (/opt/node-bridge/bridge-server-YN63DHRI.js:1:6114)
    at Object.mutateHeaders (/opt/node-bridge/bridge-server-YN63DHRI.js:2:1036)
    at /opt/node-bridge/bridge-server-YN63DHRI.js:1:887
    at /opt/node-bridge/bridge-server-YN63DHRI.js:1:4920
    at _optionalChain (/opt/node-bridge/bridge-server-YN63DHRI.js:1:865)
    at s (/opt/node-bridge/bridge-server-YN63DHRI.js:1:4842)
    at i (/var/task/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:174674)
    at U (/var/task/.next/server/chunks/1638.js:1:39498)
    at /var/task/.next/server/chunks/1638.js:1:41611
no i do not
its just an empty object
const nextConfig = {};
how does the api route response?
something like this:
 if (createdItemGroup) {
            logger.info("Created Item Group with ID:", { message: createdItemGroup.id });
            return NextResponse.json({}, { status: 200 });
        } else {
            logger.error(
                `Failed to create item group for organization ${organizationJson.id}`,
                { item: itemGroupInfo, status: 500 },
            );
            return NextResponse.json(
                { error: { message: `Failed to create item.` } },
                { status: 500 },
            );
        }
For some reason the edge function is crashing
oh wait im stupid
i forgot to rename the file, cant be exporting nothing inside a middleware.ts file
ill re-deploy and tell you
OHH IT RUNS, i stopped middleware completely and also commented out a component:

const DashboardProductDetails = dynamic(
  () => import("../components/dashboard_product_details"),
  {
    loading: () => <DashboardProductDetailsSkeleton />,
    ssr: false,
  },
);
@Clown Organization api route just has this line which uses headers: ts const secret = verifyParseBearer(req.headers.get("Authorization")); Another place is item count routes which has this abomination, this part wasnt by me iirc: ts const newHeaders = new Headers(req.headers); newHeaders.set("Authorization", `BEARER ${process.env.INTERNAL_SECRET}`); newHeaders.delete("content-length"); var organization = await fetch(API_ORGANIZATION_PATH, { headers: newHeaders, }); const organizationJson = await organization.json();
This is how im importing that component and then:
i am doing this inside:
  const itemCountRes = await fetch(API_ITEM_COUNT_PATH, {
    headers: headers(),
  });
  const itemCount = await itemCountRes.json();
  const itemGroupCountRes = await fetch(API_ITEM_GROUP_COUNT_PATH, {
    headers: headers(),
  });
  const itemGroupCount = await itemGroupCountRes.json();
oh maybe its because its ssr?
and im doing this inside
@Ray does it work? or you still getting the same error?
it works for /dashboard. however im getting same error on other child routes
@Clown oh maybe its because its ***ssr***?
this should disable ssr for client component only
Im going to try re-enable just the component to see if its atleast related to it somehow
im keeping the middleware disabled for now
ok yeah no, its happening because of this component atleast on /dashboard
on other child routes i have no idea
@Clown ok yeah no, its happening because of this component atleast on /dashboard
how do you fetch on /dashboard now?
no fetch is happening on /dashboard directly, all the fetch inside /dashboard are already as i have shown
I think the issue is with this part :/. Its happening inside the api route that the component is fetching:
 const newHeaders = new Headers(req.headers);
    newHeaders.set("Authorization", `BEARER ${process.env.INTERNAL_SECRET}`);
    newHeaders.delete("content-length");
    var organization = await fetch(API_ORGANIZATION_PATH, {
        headers: newHeaders,
    });
@Ray could you share the repo?
i can only do that privately unfortunately
Ok .. so ffs, the issue was that i cant pass the headers to fetch directly like:
{
headers: headers()
}

But instead i have to do:
{
headers: new Headers(headers())
}
Like ffs, this should be mentioned somewhere cause i spend way too much time finding this out
I always do this
{
  headers: Object.fromEntries(headers())
}
@Ray I always do this ts { headers: Object.fromEntries(headers()) }
Man they really need to add that somewhere, im sure it wouldn't have been a issue if it wasnt vercel/serverless but still a good tidbit to know
That also works as a solution