Headers cannot be modified
Answered
Clown posted this in #help-forum
ClownOP
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
The only place i could find a
Lemme know if middleware code is needed, although from the logs i can see its executing without an error.
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 codeLemme 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}`
}
})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
ClownOP
Whaaaa. Thats an issue?
@Clown 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:
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.
try this monkey
var organization = await fetch(API_ORGANIZATION_PATH, {
headers: Object.fromEntries(headers()),
});ClownOP
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
@Ray try this monkey
ts
var organization = await fetch(API_ORGANIZATION_PATH, {
headers: Object.fromEntries(headers()),
});
ClownOP
But what about that new header im setting :/
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
https://nextjs.org/docs/app/building-your-application/data-fetching/patterns
Here are some best-practices if that helps
Here are some best-practices if that helps
@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}`
}
})
ClownOP
just to confirm, you meant this right?:
var organization = await fetch(API_ORGANIZATION_PATH, {
headers: {
...Object.fromEntries(headers()),
Authorization: `BEARER ${process.env.INTERNAL_SECRET}`,
},
});@Ray use let/const instead of var <:lolsob:753870958489632819>
ClownOP
Oh yeah lol. I have pushed it, let's see if it works. I got api routes using something similar too
@Clown Nope getting the same error
hmm it work for me
API_ORGANIZATION_PATH is the external api?ClownOP
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
ClownOP
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
@Clown also i found one more instance in /dashboard routeL
tsx
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();
I just tried
headers: headers() also work@Clown I got the same thing in my middleware but i didnt change it there since its not throwing an error in it
could you try without middleware?
how does your middleware look like
let me try it
ClownOP
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*"] };ClownOP
maybe its an api route causing this :/
@Clown maybe its an api route causing this :/
could you show the code on api route too
ClownOP
Organization api route just has this line which uses headers:
Another place is item count routes which has this abomination, this part wasnt by me iirc:
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();ClownOP
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:41611how does the api route response?
ClownOP
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 },
);
}ClownOP
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();
ClownOP
This is how im importing that component and then:
i am doing this inside:
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
@Clown 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,
},
);
does it work? or you still getting the same error?
@Ray does it work? or you still getting the same error?
ClownOP
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
ClownOP
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?
ClownOP
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,
});could you share the repo?
@Ray could you share the repo?
ClownOP
i can only do that privately unfortunately
ClownOP
Ok .. so ffs, the issue was that i cant pass the headers to fetch directly like:
But instead i have to do:
{
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())
}
ClownOP
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
ClownOP
That also works as a solution
