Search code examples
next.jssupabasenextjs-dynamic-routingnext.js14drizzle

async generateMetadata function in Next.js 14.1.0 causes infinite rerenders of server components


I have a nextjs 14.1.0 app, that uses drizzle to connect to supabase.

I have a module with a dynamic param - /[workspace_id] . The folder structure is more like this src/app/(workspaces)/[workspace_id] if that matters in any way.

In the layout.tsx file, I query supabase via drizzle to get this workspace by ID. The layout has a console.log so that i can illustrate the error.

export default async function WorkspaceLayout({
  children,
  params,
}: LayoutProps) {
  console.log('🏗️ Layout')
  const { data: workspace } = await getWorkspace(params.workspace_id)

  if (!workspace?.id || workspace?.status == false) {
    return notFound()
  }

  return (
    <div data-workspace-name={workspace?.name}>
      {children}
    </div>
  )
}

Nothing too fancy. I also have a page that then lists some content from the workspace, but even if I return a simple div and a console log, the error persists.

export default async function WorkspacePage({
  params
}: {
  params: { workspace_id: string }
}) {
  console.log('📄 Page')

  return (
    <div>
        page
    </div>
  )
}

Everything works fine so far, I can navigiate in and out of that page.

The error, however, occurs when I try to add the generateMetadata function:

export async function generateMetadata(params): Promise<Metadata> {
  const workspace = await getWorkspace(params.workspace_id)

  if (!workspace?.id) {
    return notFound() // tried with null aswell, same result
  }

  return {
    title: {
      default: `${workspace?.name}`,
      template: `%s | ${workspace?.name} | mysite.com`,
    },
  }
}

And then attempt to access the page client side. I get this result: Screenshot of my terminal

The page rerenders multiple times and does not stop for quite some time. The browser shows the contents of the loading.tsx file, and the terminal just spams this message. After I navigate to another page or hard refresh this page, I get the following message as many times as I received the 📄 Page log.

Internal error: TypeError [ERR_INVALID_STATE]: Invalid state: ReadableStream is already closed
    at ReadableByteStreamController.enqueue (node:internal/webstreams/readablestream:1175:13)
    at /Users/dev/mysite/draft/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:189092
    at u (/Users/dev/mysite/draft/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:189278)
    at eH (/Users/dev/mysite/draft/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:205314)
    at ez (/Users/dev/mysite/draft/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:205101)
    at Timeout._onTimeout (/Users/dev/mysite/draft/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:201570)
    at listOnTimeout (node:internal/timers:573:17)
    at process.processTimers (node:internal/timers:514:7)

Note: after hard refresh and getting the above logs, the page then loads fine. One log of 🏗️ Layout and then one log of 📄 Page.

Before I got to the conclusion that the generateMetadata function did that, I did some checking in the form of:

  1. Disabling middleware (supabase auth).
  2. Commenting out every component in the page file and in the layout file (headers, footers, fetching components, etc.)
  3. Run the project with node 18.x and node 20.x. Running it with npm and bun, turbopack and webpack. Same result.

And after I commented out the generateMetadata funcion, the project started working normally.

To add to the problem, every single route, that is nested under this layout also has the same problem. After removing the funciton, the problem resolves. But the titles and descriptions I have are dynamic and I need to generate them base on the workspace.

Is there something I am doing wrong, or is this a bug with nextjs?


Solution

  • With 2 months of development and testing, the problem appears to be in Turbopack, or next dev --trubo. Using webpack appears to solve the problem for now.