Repository: vercel/next.js
Pull request: Fix required-server-files

Fixes a regression in https://github.com/vercel/next.js/pull/86830

`config.distDir` was missing

<img width="1622" height="475" alt="Bildschirmfoto 2025-12-05 um 17 50 43" src="https://github.com/user-attachments/assets/6460801d-949e-441b-a7d3-6fb679f22d83" />



Changed files:
filename: packages/next/src/build/index.ts
patch: @@ -1931,17 +1931,22 @@ export default async function build(
 
           if (hasInstrumentationHook) {
             serverFilesManifest.files.push(
-              path.join(SERVER_DIRECTORY, `${INSTRUMENTATION_HOOK_FILENAME}.js`)
+              path.join(
+                config.distDir,
+                SERVER_DIRECTORY,
+                `${INSTRUMENTATION_HOOK_FILENAME}.js`
+              )
             )
             // If there are edge routes, append the edge instrumentation hook
             // Turbopack generates this chunk with a hashed name and references it in middleware-manifest.
             let edgeInstrumentationHook = path.join(
+              config.distDir,
               SERVER_DIRECTORY,
               `edge-${INSTRUMENTATION_HOOK_FILENAME}.js`
             )
             if (
               bundler !== Bundler.Turbopack &&
-              existsSync(path.join(distDir, edgeInstrumentationHook))
+              existsSync(path.join(dir, edgeInstrumentationHook))
             ) {
               serverFilesManifest.files.push(edgeInstrumentationHook)
             }

filename: test/lib/next-modes/next-start.ts
patch: @@ -217,7 +217,7 @@ export class NextStartInstance extends NextInstance {
       )
     }
 
-    return new Promise<{
+    let result = await new Promise<{
       exitCode: NodeJS.Signals | number | null
       cliOutput: string
     }>((resolve) => {
@@ -238,6 +238,21 @@ export class NextStartInstance extends NextInstance {
         })
       })
     })
+
+    this._buildId = (
+      await fs
+        .readFile(
+          path.join(
+            this.testDir,
+            this.nextConfig?.distDir || '.next',
+            'BUILD_ID'
+          ),
+          'utf8'
+        )
+        .catch(() => '')
+    ).trim()
+
+    return result
   }
 
   public async waitForMinPrerenderAge(minAgeMS: number): Promise<void> {

filename: test/production/standalone-mode/required-server-files/instrumentation.js
patch: @@ -0,0 +1 @@
+export function register() {}

filename: test/production/standalone-mode/required-server-files/required-server-files.test.ts
patch: @@ -44,6 +44,9 @@ describe('required server files', () => {
               : 'middleware.js'
           )
         ),
+        'instrumentation.js': new FileRef(
+          join(__dirname, 'instrumentation.js')
+        ),
         'cache-handler.js': new FileRef(join(__dirname, 'cache-handler.js')),
         'data.txt': new FileRef(join(__dirname, 'data.txt')),
         '.env': new FileRef(join(__dirname, '.env')),
@@ -84,8 +87,11 @@ describe('required server files', () => {
           }
         },
       },
+      skipStart: true,
     })
-    await next.stop()
+
+    let { exitCode } = await next.build()
+    expect(exitCode).toBe(0)
 
     requiredFilesManifest = JSON.parse(
       await next.readFile('.next/required-server-files.json')
@@ -241,11 +247,11 @@ describe('required server files', () => {
           redirect: 'manual',
         }
       )
-      expect(dataRes.headers.get('cache-control')).toBe(cacheControl)
       expect((await dataRes.json()).pageProps).toEqual({
         __N_REDIRECT: dest,
         __N_REDIRECT_STATUS: 307,
       })
+      expect(dataRes.headers.get('cache-control')).toBe(cacheControl)
     }
   )
 
@@ -415,6 +421,12 @@ describe('required server files', () => {
     expect(typeof requiredFilesManifest.appDir).toBe('string')
     // not in a monorepo so relative app dir is empty string
     expect(requiredFilesManifest.relativeAppDir).toBe('')
+
+    expect(
+      requiredFilesManifest.files.filter(
+        (f) => !fs.pathExistsSync(join(next.testDir, 'standalone', f))
+      )
+    ).toBeEmpty()
   })
 
   it('should de-dupe HTML/data requests', async () => {