Skip to content

Commit f9fd199

Browse files
committed
Throw when generateStaticParams returns incomplete params with output: export
1 parent 8a61212 commit f9fd199

5 files changed

Lines changed: 62 additions & 12 deletions

File tree

errors/generate-static-params.mdx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ When your application uses `output: 'export'`, every [Dynamic Route](/docs/app/a
2929

3030
This error also occurs when a dynamic route:
3131

32-
- does not export a `generateStaticParams` function, or
33-
- returns an empty array from `generateStaticParams`.
32+
- does not export a `generateStaticParams` function,
33+
- returns an empty array from `generateStaticParams`, or
34+
- generates a route without every dynamic route parameter. For example, `{ slug: 'first' }` does not provide the `id` value for a route using an `[id]` segment.
3435

35-
Export `generateStaticParams` from every dynamic route and return at least one params object. If the paths cannot be known at build time, remove `output: 'export'` from your Next.js configuration.
36+
When multiple route segments export `generateStaticParams`, Next.js combines the params returned by each parent and child function before generating routes.
37+
38+
Export `generateStaticParams` from every dynamic route and make sure the combined params include all dynamic route parameters for every generated route. If the paths cannot be known at build time, remove `output: 'export'` from your Next.js configuration.
3639

3740
## Useful Links
3841

packages/next/errors.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1451,5 +1451,6 @@
14511451
"1450": "Invalid value at index %s returned from generateStaticParams for \"%s\". Expected an object, but received type %s. See more info here: https://nextjs.org/docs/messages/generate-static-params",
14521452
"1451": "Page \"%s\" is missing \"generateStaticParams()\" so it cannot be used with \"output: export\" config. See more info here: https://nextjs.org/docs/messages/generate-static-params",
14531453
"1452": "Page \"%s\" is missing exported function \"generateStaticParams()\", which is required with \"output: export\" config. See more info here: https://nextjs.org/docs/messages/generate-static-params",
1454-
"1453": "Page \"%s\" returned an empty array from \"generateStaticParams()\". With \"output: export\", at least one route must be generated. See more info here: https://nextjs.org/docs/messages/generate-static-params"
1454+
"1453": "Page \"%s\" returned an empty array from \"generateStaticParams()\". With \"output: export\", at least one route must be generated. See more info here: https://nextjs.org/docs/messages/generate-static-params",
1455+
"1454": "Page \"%s\" returned incomplete params from \"generateStaticParams()\". With \"output: export\", every params object must include all dynamic route parameters. Missing: %s. See more info here: https://nextjs.org/docs/messages/generate-static-params"
14551456
}

packages/next/src/build/static-paths/app.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -977,17 +977,29 @@ export async function buildAppStaticPaths({
977977
}
978978
}
979979

980+
const missingParamNames: string[] = []
981+
if (routeParams.length > 0) {
982+
for (const { paramName } of pathnameRouteParamSegments) {
983+
if (routeParams.some((params) => !(paramName in params))) {
984+
missingParamNames.push(paramName)
985+
}
986+
}
987+
}
988+
980989
// Determine if all the segments have had their parameters provided.
981990
const hadAllParamsGenerated =
982991
pathnameRouteParamSegments.length === 0 ||
983-
(routeParams.length > 0 &&
984-
routeParams.every((params) => {
985-
for (const { paramName } of pathnameRouteParamSegments) {
986-
if (paramName in params) continue
987-
return false
988-
}
989-
return true
990-
}))
992+
(routeParams.length > 0 && missingParamNames.length === 0)
993+
994+
if (
995+
nextConfigOutput === 'export' &&
996+
routeParams.length > 0 &&
997+
!hadAllParamsGenerated
998+
) {
999+
throw new Error(
1000+
`Page "${page}" returned incomplete params from "generateStaticParams()". With "output: export", every params object must include all dynamic route parameters. Missing: ${missingParamNames.map((name) => `"${name}"`).join(', ')}. See more info here: https://nextjs.org/docs/messages/generate-static-params`
1001+
)
1002+
}
9911003

9921004
// TODO: dynamic params should be allowed to be granular per segment but
9931005
// we need additional information stored/leveraged in the prerender

test/e2e/app-dir-export/test/dynamic-missing-gsp.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,24 @@ describe('app dir - with output export - dynamic missing gsp', () => {
3939
})
4040
})
4141

42+
describe('should error when generateStaticParams returns incomplete params', () => {
43+
runTests({
44+
dynamicPage: 'undefined',
45+
generateStaticParamsOpt: 'set wrong param',
46+
expectedErrMsg:
47+
'Page "/another/[slug]" returned incomplete params from "generateStaticParams()". With "output: export", every params object must include all dynamic route parameters. Missing: "slug". See more info here: https://nextjs.org/docs/messages/generate-static-params',
48+
})
49+
})
50+
51+
describe('should error when one of the generated params is incomplete', () => {
52+
runTests({
53+
dynamicPage: 'undefined',
54+
generateStaticParamsOpt: 'set mixed params',
55+
expectedErrMsg:
56+
'Page "/another/[slug]" returned incomplete params from "generateStaticParams()". With "output: export", every params object must include all dynamic route parameters. Missing: "slug". See more info here: https://nextjs.org/docs/messages/generate-static-params',
57+
})
58+
})
59+
4260
describe('should error when client component has generateStaticParams', () => {
4361
const expectedErrMsg = process.env.IS_TURBOPACK_TEST
4462
? 'App pages cannot use both "use client" and export function "generateStaticParams()".'

test/e2e/app-dir-export/test/utils.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,9 @@ export function runTests({
222222
| 'set client'
223223
| 'set empty'
224224
| 'set invalid entry'
225+
| 'set mixed params'
225226
| 'set non-array'
227+
| 'set wrong param'
226228
expectedErrMsg?: string | RegExp
227229
}) {
228230
let { next, skipped, isNextDev } = nextTestSetup({
@@ -302,6 +304,20 @@ export function runTests({
302304
'return []'
303305
)
304306
)
307+
} else if (generateStaticParamsOpt === 'set wrong param') {
308+
await next.patchFile('app/another/[slug]/page.js', (content) =>
309+
content.replace(
310+
`return [{ slug: 'first' }, { slug: 'second' }]`,
311+
`return [{ id: 'first' }]`
312+
)
313+
)
314+
} else if (generateStaticParamsOpt === 'set mixed params') {
315+
await next.patchFile('app/another/[slug]/page.js', (content) =>
316+
content.replace(
317+
`return [{ slug: 'first' }, { slug: 'second' }]`,
318+
`return [{ slug: 'first' }, { id: 'second' }]`
319+
)
320+
)
305321
}
306322
})
307323

0 commit comments

Comments
 (0)