Dynamic Server Error
Answered
Dreamgineer posted this in #help-forum
I have a nextjs app and a route that serve files. The app builts perfectly with no error, it deployed perfectly, everything works perfectly except that route.
Requesting to that route throws
This is the error:
Requesting to that route throws
DYNAMIC_SERVER_ERROR no matter which file.This is the error:
app-1 | ⨯ Error: Dynamic server usage: Route is configured with methods that cannot be statically generated.
app-1 | at processTicksAndRejections (null) {
app-1 | description: 'Route is configured with methods that cannot be statically generated.',
app-1 | digest: 'DYNAMIC_SERVER_USAGE'
app-1 | }45 Replies
Related code:
import { eq } from "drizzle-orm";
import { notFound, redirect } from "next/navigation";
import { checkExpiry, rateLimit } from "@/lib/api";
import { db, resolve } from "@/lib/db";
import { garage } from "@/lib/db/storage";
import { createDecryptor, cryptStream } from "@/lib/sec";
export async function generateStaticParams() {
return [];
}
export async function GET(
{ headers }: NextRequest,
{ params }: { params: Promise<{ name: string[] }> },
) {
const { name } = await params;
const path = name.join("/");
await rateLimit(path); // this function calls `await headers();`
await checkExpiry();
const entry = await resolve(path);
if (!entry) {
await rateLimit(path, true);
// Only show 404 page to browsers
if ((headers.get("accept") || "").includes("text/html")) redirect("/404");
else notFound();
}
if (entry.kind === "link") redirect(entry.href);
if (entry.kind === "bundle") redirect(`/bundle/${entry.id}`);
if (entry.encrypted) redirect(`/unlock/${entry.path}`);
if (!entry.object_id)
return new NextResponse("This file is still being uploaded...", {
headers: { "Retry-After": "30" },
status: 409,
});
const file = garage.file(entry.object_id);
// This forces download(no preview) when
// - File is hidden, so that name becomes the original instead of ID
// - File is not a media or pdf, but is bigger than 500kb, to prevent browser
lagging
// - File doesn't have a MIME type
return new NextResponse(
file.stream().pipeThrough(cryptStream(createDecryptor(entry.key))),
{
headers: {
"Content-Type": entry.type,
...(entry.hidden ||
!entry.type ||
(!["application/pdf", "video/", "image/"].some((t) =>
entry.type.startsWith(t),
) &&
entry.size > 500_000)
? {
"Content-Disposition": `attachment; filename=${JSON.stringify(file.name)}`,
}
: {}),
},
},
);
}@alfonsüs ardani can you send your build log
▲ Next.js 16.1.1 (Turbopack)
- Environments: .env.local, .env
- Experiments (use with caution):
✓ authInterrupts
✓ typedEnv
Creating an optimized production build ...
✓ Compiled successfully in 7.7s
✓ Finished TypeScript in 11.4s
✓ Collecting page data using 3 workers in 663.1ms
✓ Generating static pages using 3 workers (8/8) in 401.0ms
✓ Finalizing page optimization in 22.1ms
Route (app)
┌ ○ /
├ ○ /_not-found
├ ● /[...name]
├ ○ /404
├ ƒ /admin
├ ƒ /admin/upload
├ ƒ /api/file
├ ƒ /api/file/[name]
├ ƒ /bundle/[id]
├ ○ /icon.png
└ ● /unlock/[...name]
○ (Static) prerendered as static content
● (SSG) prerendered as static HTML (uses generateStaticParams)
ƒ (Dynamic) server-rendered on demandand which route throws that error?
@Dreamgineer `/[...name]`
that route is rendered as Static
Answer
it doesn't throw error at build because you tell nextjs to statically prerender 0 route (
generateStaticParams()) so it doesn't run any of the codethis is a feature if you want Incremental Static Generation (Static Generation at request time)
soo
if you need dynamic features, try removing
generateStaticParams()How did I not think of that after 3 hours bruh
bcs nextjs is good at mis-assuming what you wanted
So I accidentally force static'd it
you didn't force static'd it, i mean i would say its not the "forceful" way to do it.
u jsut accidentally opted in into it
u jsut accidentally opted in into it

the forceful way to do it is:
just fyi
this override all automatic infer/opt-in/opt-out shenanigan
export const dynamic = 'force-static'just fyi
this override all automatic infer/opt-in/opt-out shenanigan
Well the only reason it was dynamic is because of the ratelimit function 😭
Without that function the links would be scannable
I could move it to proxy.ts though
And I could keep the speed of ISR
yes i was about to say that
if you are ok with using memory-based rate-limit then doing it in proxy.ts is viable
Wait
But ratelimit relies on the response too
Since it flags based on 404s, not just 200s
@Dreamgineer And I could keep the speed of ISR
you can keep the speed of ISR even if the GET() function is dynamically rendered and alfon would love to tell you more about this
Wait
rateLimit + the blank ISR was there since last version and it works
eitherway it wouldn't make sense since the ratelimit would only run once even user accessed that route 100 times no?
I saw buncha IPs on my redis server so it must've worked
do you still have the build log of the previous version? was it
● or was it ƒ?I don't
then lets not dwell on the past 

@Dreamgineer so what is it
you cache the function and leave out the
await params or await headers outside of the functionwait
I realized something
I think neither ISR nor cache is gonna work on streamed download
are you sure that cache wont work?
The file size is up to 5gb
I should just stream to both response and a file system cache with TTL
sounds like a good plan