A metadata export inside a 'use client' page is one of the most common silent failures in AI-generated Next.js code: the model writes both directives in the same file, Next.js drops the metadata without any warning, and the page ships with no <title> or <meta description>. The same applies to generateStaticParams placed in a component file — it runs nowhere. CWE-489 (Active Debug Code) maps loosely, but the practical harm is SEO blackout and broken static generation — neither of which surfaces in development because the app still renders.
Medium because the failure is silent data loss (missing metadata, skipped static params) rather than a security vulnerability, but it directly harms SEO and deployment correctness.
Split 'use client' pages that export metadata into a server wrapper and a client component. Move all convention exports to the server file.
// app/dashboard/page.tsx — server component (no 'use client')
export const metadata = { title: 'Dashboard', description: 'Your overview' }
export { default } from './dashboard-client'
// app/dashboard/dashboard-client.tsx
'use client'
import { useState } from 'react'
export default function Dashboard() {
const [count, setCount] = useState(0)
return <div>{count}</div>
}
For dynamic or revalidate found in component files under src/components/, move the export to the page.tsx, layout.tsx, or route.ts that imports the component. Config exports have no effect in component files.
ID: ai-slop-hallucinations.route-references.page-conventions-in-correct-files
Severity: medium
What to look for: When the framework is Next.js, scan all .ts/.tsx files under src/, app/, lib/, components/, hooks/, utils/. For each file, extract every named export (export const X, export function X, export async function X). Check each export name against this list of Next.js convention names that are ONLY meaningful in specific file types: metadata, generateMetadata, generateStaticParams, dynamic, revalidate, fetchCache, runtime, preferredRegion, maxDuration. For each convention export found, verify it is in a correct file location: (a) metadata and generateMetadata — valid ONLY in Server Component page.tsx or layout.tsx files (files under app/ named page.* or layout.* that do NOT have 'use client' as the first directive). If a page.tsx or layout.tsx has 'use client' at the top and exports metadata or generateMetadata, flag it — Next.js silently ignores metadata exports from Client Components. (b) generateStaticParams — valid ONLY in page.tsx or layout.tsx with a [param] dynamic segment in its path. (c) dynamic, revalidate, fetchCache, runtime, preferredRegion, maxDuration — valid in page.tsx, layout.tsx, and route.ts files (these work in both Server and Client Components). Flag any convention export found in files that are NOT page/layout/route convention files (e.g., in components/, hooks/, utils/, lib/). Do NOT flag internal variables named metadata that are not exported — only flag export const metadata / export function generateMetadata patterns. Count all convention exports inspected, total in correct locations, total misplaced.
Detector snippet (Node-capable tools only): If the tool has shell + Node, scan every source file for the 'use client' directive and, in any file that has it, look for misplaced Next.js route-convention exports (metadata, generateMetadata, dynamic, revalidate, generateStaticParams, etc.). Output reports misplaced count. If exit 0 with "misplaced=0", report pass. If "misplaced>0", report fail. Exit >=2 or command not found — fall back to prose reasoning below.
node -e 'const fs=require("fs"),path=require("path");const EX=["metadata","generateMetadata","dynamic","revalidate","generateStaticParams","dynamicParams","fetchCache","runtime"];function w(d,o=[]){try{for(const e of fs.readdirSync(d,{withFileTypes:true})){if(e.name==="node_modules"||e.name===".next")continue;const p=path.join(d,e.name);e.isDirectory()?w(p,o):o.push(p)}}catch{}return o}let bad=[];for(const f of w("src")){if(!/\.(ts|tsx|js|jsx)$/.test(f))continue;const s=fs.readFileSync(f,"utf-8");if(!/[\u0022\u0027]use client[\u0022\u0027]/.test(s))continue;for(const ex of EX){const re=new RegExp("export\\s+(const|async\\s+function|function)\\s+"+ex+"\\b");if(re.test(s)){bad.push(f+": "+ex);break}}}console.log("misplaced="+bad.length);bad.slice(0,5).forEach(x=>console.log(x))'
Pass criteria: 0 convention exports are in incorrect file locations. Report: "X convention exports inspected, Y in correct locations, 0 misplaced."
Fail criteria: At least 1 convention export is in a file where Next.js silently ignores it.
Do NOT pass when: A 'use client' page or layout exports metadata or generateMetadata — this is a common AI error where the model adds metadata to a Client Component, and Next.js silently drops it without any build warning.
Skip (N/A) when: Framework is not Next.js OR no convention exports found in any source file.
Detail on fail: "2 misplaced convention exports: 'export const metadata' in src/app/dashboard/page.tsx (page has 'use client' — metadata is silently ignored in Client Components), 'export const dynamic' in src/components/DataTable.tsx (only valid in page/layout/route files)."
Remediation: Next.js convention exports are silently ignored when placed in the wrong file type — no build error, no runtime warning, just missing behavior. Fix each one:
// Bad: metadata in a 'use client' page is silently ignored
'use client'
export const metadata = { title: 'Dashboard' } // ← ignored!
// Good: split into a server wrapper
// app/dashboard/page.tsx (server component)
export const metadata = { title: 'Dashboard' }
export { default } from './dashboard-client'
// app/dashboard/dashboard-client.tsx
'use client'
export default function Dashboard() { /* ... */ }
For config exports like dynamic or revalidate found in component files, move them to the page/layout/route file that imports the component.