Server action revalidateTag error
Answered
Arboreal ant posted this in #help-forum
Arboreal antOP
Hey all,
I have the following server action which makes a fetch request to our external api (via a next.config
For some reason having
How would I go about debugging this and trying to fix it?
I have the following server action which makes a fetch request to our external api (via a next.config
rewrites proxy)export async function updateUserProfile(userId: string, data: UserProfilePatch): Promise<UserProfile> {
"use server"
const updatedUser = await usersUserIdProfilePatch({pathParams: {userId}, body: data });
revalidateTag(`usersUserIdProfileGet${userId}`);
return updatedUser;
}For some reason having
revalidateTag is causing the following error to be thrown:frontend:dev: ⨯ Internal error: TypeError: fetch failed
frontend:dev: at Object.fetch (node:internal/deps/undici/undici:11730:11)
frontend:dev: Cause: RequestContentLengthMismatchError: Request body length does not match content-length header
frontend:dev: at write (node:internal/deps/undici/undici:8590:41)
frontend:dev: at _resume (node:internal/deps/undici/undici:8563:33)
frontend:dev: at resume (node:internal/deps/undici/undici:8459:7)
frontend:dev: at connect (node:internal/deps/undici/undici:8446:7) {
frontend:dev: code: 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH'
frontend:dev: }How would I go about debugging this and trying to fix it?
Answered by Arboreal ant
For future readers or searchers of this topic:
I've figured out what was causing the 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH' error and managed to get revalidation working again!
After bumping from v14.0.4 to v14.1.0 the fetch error messages changed and became useful! It turns out some other requests were being made which were failing also with the same error.
(side note: whoever developed https://nextjs.org/blog/next-14-1#improved-error-messages-and-fast-refresh completely saved my sanity. Massive Thanks to you!!!)
It turns out that when revalidating different headers are being sent with the request by next. The fix was fairly simple after realising that. I was only really interesting in passing the cookies header along. So I changed all my requests from:
to
And suddenly everything is working again!
I've figured out what was causing the 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH' error and managed to get revalidation working again!
After bumping from v14.0.4 to v14.1.0 the fetch error messages changed and became useful! It turns out some other requests were being made which were failing also with the same error.
(side note: whoever developed https://nextjs.org/blog/next-14-1#improved-error-messages-and-fast-refresh completely saved my sanity. Massive Thanks to you!!!)
It turns out that when revalidating different headers are being sent with the request by next. The fix was fairly simple after realising that. I was only really interesting in passing the cookies header along. So I changed all my requests from:
import { headers } from 'next/headers';
...
const res = await authUserInfoGetFetch({
headers: new Headers(headers()),
...requestInitArgs
});to
import { cookies } from 'next/headers';
...
const reqHeaders = new Headers();
const reqCookies = cookies();
reqHeaders.set('cookie', reqCookies.toString());
const res = await authUserInfoGetFetch({
headers: reqHeaders,
...requestInitArgs
});And suddenly everything is working again!
65 Replies
@Arboreal ant Hey all,
I have the following server action which makes a fetch request to our external api (via a next.config `rewrites` proxy)
ts
export async function updateUserProfile(userId: string, data: UserProfilePatch): Promise<UserProfile> {
"use server"
const updatedUser = await usersUserIdProfilePatch({pathParams: {userId}, body: data });
revalidateTag(`usersUserIdProfileGet${userId}`);
return updatedUser;
}
For some reason having `revalidateTag` is causing the following error to be thrown:
ts
frontend:dev: ⨯ Internal error: TypeError: fetch failed
frontend:dev: at Object.fetch (node:internal/deps/undici/undici:11730:11)
frontend:dev: Cause: RequestContentLengthMismatchError: Request body length does not match content-length header
frontend:dev: at write (node:internal/deps/undici/undici:8590:41)
frontend:dev: at _resume (node:internal/deps/undici/undici:8563:33)
frontend:dev: at resume (node:internal/deps/undici/undici:8459:7)
frontend:dev: at connect (node:internal/deps/undici/undici:8446:7) {
frontend:dev: code: 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH'
frontend:dev: }
How would I go about debugging this and trying to fix it?
Elm sawfly
tag: A string representing the cache tag associated with the data you want to revalidate. Must be less than or equal to 256 characters. This value is case-sensitive.
https://nextjs.org/docs/app/api-reference/functions/revalidateTag#parameters
https://nextjs.org/docs/app/api-reference/functions/revalidateTag#parameters
Arboreal antOP
Oh so I can't tag based on specific routes?
Elm sawfly
You need to use
revalidatePathArboreal antOP
Ok I'll give that a try 🙂 I was hoping to use the op-id and userId to save having to export the paths.
Should revalidatePath include the query params too if they exist? Does that have a max length too?
@Arboreal ant Ok I'll give that a try 🙂 I was hoping to use the op-id and userId to save having to export the paths.
Elm sawfly
and as far as I can understand your code
You are updating your user's profile
You are updating your user's profile
Arboreal antOP
A users profile, not necessarily the logged in user
Elm sawfly
If a user is not logged in, how is he/she updating their profile?
Arboreal antOP
And the logged in user could have lots of user profiles fetched. For things like avatars etc.
If an admin of the users organisation updates it.
Or a site admin.
Elm sawfly
Ok wait
What are you trying to achieve?
revalidatePath and revalidateTag would help you to refresh your data of that pageso if you have some cached data, it will get refreshed
Arboreal antOP
I'd like to refetch
/users/1/profile when user 1's profile is updatedon this page, and on a couple of other spots which could be fetching that profile too
For the current user*
Elm sawfly
Okay
So it should be like this
So it should be like this
revalidatePath(`/users/${userId}/profile`)Arboreal antOP
Cool I'll try that now 🙂
For other (future) routes, what happens if the original fetch had query params? I assume 'revalidatePath' means they'll be refetched also because the query params aren't part of the path?
the url you will provide
only that path's cache will get cleaned and refreshed
Arboreal antOP
Sorry I probably wasn't clear.
If in componentA I had
and in componentB I ran
Would componentA's fetch be invalidated? Or is it an exact match?
If in componentA I had
fetch('/someRoute/1/info?query=aand in componentB I ran
revalidatePath('/someRoute/1/info')Would componentA's fetch be invalidated? Or is it an exact match?
Hmm I'm still getting a "UND_ERR_REQ_CONTENT_LENGTH_MISMATCH" error in the refetch.
because the URL which you are not providing should not get revalidated
@Arboreal ant Hmm I'm still getting a "UND_ERR_REQ_CONTENT_LENGTH_MISMATCH" error in the refetch.
Elm sawfly
share your code
Arboreal antOP
It's triggering it still, I'm seeing the
usersUserIdProfileGetFetch function get called. But the fetch is failing for some reason.1 mo let me clean up some debug logs
Elm sawfly
ok
Arboreal antOP
#/dashboard/users/[userId]/page.tsx
export async function updateUserProfile(userId: string, data: UserProfilePatch): Promise<UserProfile> {
"use server"
const updatedUser = await usersUserIdProfilePatch({pathParams: {userId}, body: data });
revalidatePath(`${baseUrl}/users/${encodeURIComponent(userId)}/profile`)
return updatedUser;
}
export default async function ManageUserProfilePage({ params }: { params: { userId: string } }): Promise<JSX.Element> {
const userProfile = await usersUserIdProfileGet({
pathParams: {
userId: params.userId,
},
});
return (
<div className='w-full p-6'>
<Typography as='h2' variant={'h2'}>
Manage Profile
</Typography>
<ManageUserProfile userProfile={userProfile} updateUserProfile={updateUserProfile} />
</div>
);
}# ManageUserProfile.tsx
export const ManageUserProfile: FC<ManageUserProfileProps> = ({ userProfile, updateUserProfile }) => {
const onSubmit = (data: ProfileFormValues) => {
updateUserProfile(userProfile.userId ?? '', {
bio: data.bio
});
};
...# Server side get fn
export const usersUserIdProfileGet = async ({
pathParams,
...requestInitArgs
}:{
pathParams: {
userId: string,
},
} & RequestInit): Promise<UserProfile> => {
const res = await usersUserIdProfileGetFetch({
pathParams,
headers: new Headers(headers()),
...requestInitArgs
});
if(res.status === 401) {
redirect(signInUrl);
}
const responseBody = await res.json();
return responseBody as UserProfile;
};# fetcher
export const usersUserIdProfileGetFetch = async ({
pathParams,
...requestInitArgs
}:{
pathParams: {
userId: string,
},
} & Omit<RequestInit, 'body'>): Promise<HttpResponseType<UserProfile>> => {
const { headers: reqHeaders, ...requestInitArgsRest } = requestInitArgs ?? {};
let headers: Headers;
if(reqHeaders) {
headers = new Headers(reqHeaders);
} else {
headers = new Headers();
}
const res = await fetch(`${baseUrl}/users/${encodeURIComponent(pathParams.userId)}/profile`, {
headers,
method: 'GET',
...requestInitArgsRest
});
return res;
};Elm sawfly
and what's the error?
Arboreal antOP
Hmm would that matter? The json is parsed in the higher level function?
const responseBody = await res.json();I wanted a generic fetch so any client side components could also use these fetches. But I wanted the server side components to be able to pass in headers (for cookies) and to be able to handle 401's.
Elm sawfly
I see
can you show me the error
Arboreal antOP
It's failing before the json anyway.
console.log('fetching')
const res = await fetch(`${baseUrl}/users/${encodeURIComponent(pathParams.userId)}/profile`, {
headers,
method: 'GET',
...requestInitArgsRest
});
console.log('fetch finished');fetching
frontend:dev: ⨯ Internal error: TypeError: fetch failed
frontend:dev: at Object.fetch (node:internal/deps/undici/undici:11730:11)
frontend:dev: Cause: RequestContentLengthMismatchError: Request body length does not match content-length header
frontend:dev: at write (node:internal/deps/undici/undici:8590:41)
frontend:dev: at _resume (node:internal/deps/undici/undici:8563:33)
frontend:dev: at resume (node:internal/deps/undici/undici:8459:7)
frontend:dev: at connect (node:internal/deps/undici/undici:8446:7) {
frontend:dev: code: 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH'
frontend:dev: }
frontend:dev: ⨯ Internal error: TypeError: fetch failed
frontend:dev: at Object.fetch (node:internal/deps/undici/undici:11730:11)
frontend:dev: Cause: RequestContentLengthMismatchError: Request body length does not match content-length header
frontend:dev: at write (node:internal/deps/undici/undici:8590:41)
frontend:dev: at _resume (node:internal/deps/undici/undici:8563:33)
frontend:dev: at resume (node:internal/deps/undici/undici:8459:7)
frontend:dev: at connect (node:internal/deps/undici/undici:8446:7) {
frontend:dev: code: 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH'
frontend:dev: }Elm sawfly
Cause: RequestContentLengthMismatchError: Request body length does not match content-length header
Arboreal antOP
I'm not sure why a get request has a body anyway.
frontend:dev: fetching
frontend:dev: {
frontend:dev: headers: HeadersList {
frontend:dev: cookies: null,
frontend:dev: [Symbol(headers map)]: Map(21) {
frontend:dev: 'accept' => [Object],
frontend:dev: 'accept-encoding' => [Object],
frontend:dev: 'accept-language' => [Object],
frontend:dev: 'cache-control' => [Object],
frontend:dev: 'connection' => [Object],
frontend:dev: 'content-length' => [Object],
frontend:dev: 'content-type' => [Object],
frontend:dev: 'cookie' => [Object],
frontend:dev: 'host' => [Object],
frontend:dev: 'next-action' => [Object],
frontend:dev: 'origin' => [Object],
frontend:dev: 'pragma' => [Object],
frontend:dev: 'referer' => [Object],
frontend:dev: 'sec-fetch-dest' => [Object],
frontend:dev: 'sec-fetch-mode' => [Object],
frontend:dev: 'sec-fetch-site' => [Object],
frontend:dev: 'user-agent' => [Object],
frontend:dev: 'x-forwarded-for' => [Object],
frontend:dev: 'x-forwarded-host' => [Object],
frontend:dev: 'x-forwarded-port' => [Object],
frontend:dev: 'x-forwarded-proto' => [Object]
frontend:dev: },
frontend:dev: [Symbol(headers map sorted)]: null
frontend:dev: },
frontend:dev: contentLength: undefined,
frontend:dev: method: 'GET',
frontend:dev: requestInitArgsRest: {}
frontend:dev: }
frontend:dev: ⨯ Internal error: TypeError: fetch failedThere's no body being passed in, and content-length isn't set. Could that undefined be causing an issue?
Elm sawfly
Maybe
Tbh never encountered something like this
Tbh never encountered something like this
so ya I am also trying my best to understand
Arboreal antOP
Me neither. Once I get everything working next should be a lot simpler, but atm it's a lot more complex lol.
I added some logs and the successful calls that happen before the patch to that route also have undefined as the content-length.
Elm sawfly
Ohh
Arboreal antOP
Thanks for helping out btw. I really appreciate it.
Elm sawfly
I hope any other senior developer could help
Arboreal antOP
I think I might have spotted something
//
SUCCESS HEADERS:
accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
// accept-encoding=gzip, deflate, br
// accept-language=en-GB,en;q=0.5
// cache-control=no-cache
// connection=keep-alive
// cookie=ccp-session=s%3A9WzTLTz6QUZ0JBciY_FshmyKDJzKGGyG.fDlWP0scmzKySz3hyoCNyxbSGhI8y5slB6Y8G%2FlnYbc
// host=localhost:3000
// pragma=no-cache
// referer=http://localhost:3000/dashboard/users/d3b1d562-abd9-4c1b-a80b-dfcd6dba270f/manage-profile
// sec-fetch-dest=document
// sec-fetch-mode=navigate
// sec-fetch-site=same-origin
// upgrade-insecure-requests=1
// user-agent=Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:122.0) Gecko/20100101 Firefox/122.0
// x-forwarded-for=::ffff:127.0.0.1
// x-forwarded-host=localhost:3000
// x-forwarded-port=3000
// x-forwarded-proto=http
FAILING HEADERS:
// accept=text/x-component
// accept-encoding=gzip, deflate, br
// accept-language=en-GB,en;q=0.5
// cache-control=no-cache
// connection=keep-alive
// content-length=232
// content-type=text/plain;charset=UTF-8
// cookie=ccp-session=s%3A9WzTLTz6QUZ0JBciY_FshmyKDJzKGGyG.fDlWP0scmzKySz3hyoCNyxbSGhI8y5slB6Y8G%2FlnYbc
// host=localhost:3000
// next-action=0b3e074d8b1a22ce69289dddb8c346ad3ff1c1bd
// origin=http://localhost:3000
// pragma=no-cache
// referer=http://localhost:3000/dashboard/users/d3b1d562-abd9-4c1b-a80b-dfcd6dba270f/manage-profile?firstName=Bonnie&lastName=Em&bio=I%27ve+been+a+dedicated+Residential+Conveyancer+for+6+years.+Last+year+I+won+the+%22conveyancer+of+the+year%22+award%2C+and+successfully+saw+over+300+cases+through+to+completion.+UPDATED6
// sec-fetch-dest=empty
// sec-fetch-mode=cors
// sec-fetch-site=same-origin
// user-agent=Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:122.0) Gecko/20100101 Firefox/122.0
// x-forwarded-for=::ffff:127.0.0.1
// x-forwarded-host=localhost:3000
// x-forwarded-port=3000
// x-forwarded-proto=httpIt's only accepting text/x-component, and it has the content-length set to 232 for some reason
Arboreal antOP
Hmm even manually wrangling all the headers to be identical it still fails. I'm going to try invalidating the page routes instead of the http fetch.
Arboreal antOP
Are you sure relvalidatePath is correct?
The docs seem to imply it's for invalidating pages/layouts.
I've tried changing it to the page file paths but it's still throwing the same error 😦
The docs seem to imply it's for invalidating pages/layouts.
I've tried changing it to the page file paths but it's still throwing the same error 😦
revalidatePath(`/(platform)/dashboard/users/[userId]`, 'page');
or
revalidatePath(`/dashboard/users/[userId]`, 'page');
or 'layout' instead of 'page'A crappy fix for now will be to use unstable_noStore on all these affected components. But I need to get it fixed soon. This first sprint has been a disaster with lots of small issues like this 😦
Arboreal antOP
For future readers or searchers of this topic:
I've figured out what was causing the 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH' error and managed to get revalidation working again!
After bumping from v14.0.4 to v14.1.0 the fetch error messages changed and became useful! It turns out some other requests were being made which were failing also with the same error.
(side note: whoever developed https://nextjs.org/blog/next-14-1#improved-error-messages-and-fast-refresh completely saved my sanity. Massive Thanks to you!!!)
It turns out that when revalidating different headers are being sent with the request by next. The fix was fairly simple after realising that. I was only really interesting in passing the cookies header along. So I changed all my requests from:
to
And suddenly everything is working again!
I've figured out what was causing the 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH' error and managed to get revalidation working again!
After bumping from v14.0.4 to v14.1.0 the fetch error messages changed and became useful! It turns out some other requests were being made which were failing also with the same error.
(side note: whoever developed https://nextjs.org/blog/next-14-1#improved-error-messages-and-fast-refresh completely saved my sanity. Massive Thanks to you!!!)
It turns out that when revalidating different headers are being sent with the request by next. The fix was fairly simple after realising that. I was only really interesting in passing the cookies header along. So I changed all my requests from:
import { headers } from 'next/headers';
...
const res = await authUserInfoGetFetch({
headers: new Headers(headers()),
...requestInitArgs
});to
import { cookies } from 'next/headers';
...
const reqHeaders = new Headers();
const reqCookies = cookies();
reqHeaders.set('cookie', reqCookies.toString());
const res = await authUserInfoGetFetch({
headers: reqHeaders,
...requestInitArgs
});And suddenly everything is working again!
Answer