withastro/astro · critical · Error

[@astrojs/node] Could not find the server directory "${serve

Error message

[@astrojs/node] Could not find the server directory "${serverFolder}" by walking up from "${import.meta.url}". This can happen when the server entry point is bundled into a single file (e.g. with esbuild) so that import.meta.url no longer contains the original "${serverFolder}" path segment. When bundling the server entry, make sure the output path contains a "${serverFolder}" directory segment, or avoid bundling the server entry entirely.

What it means

Thrown by @astrojs/node's `shared.ts` when it cannot locate the server output directory (`options.server` basename, e.g. `server`) by walking up the directory tree from `import.meta.url`. The relative path from server to client assets is computed from this folder; if the path segment is missing the runtime cannot resolve static assets.

Source

Thrown at packages/integrations/node/src/shared.ts:37

export function resolveClientDir(options: Options) {
	// options.client and options.server are file:// URLs set at build time
	// e.g., "file:///project/dist/client/" and "file:///project/dist/server/"
	const clientURLRaw = new URL(options.client);
	const serverURLRaw = new URL(options.server);

	// Calculate relative path from server to client (e.g., "../client")
	// This relative path is stable regardless of where the build output is deployed
	const rel = path.relative(url.fileURLToPath(serverURLRaw), url.fileURLToPath(clientURLRaw));

	// Find the server entry folder by walking up from this file's location
	// We need to find the actual runtime location, not the build-time paths
	const serverFolder = path.basename(options.server);
	let serverEntryFolderURL = path.dirname(import.meta.url);
	let previous = '';
	while (!serverEntryFolderURL.endsWith(serverFolder)) {
		// Guard against infinite loop
		if (serverEntryFolderURL === previous) {
			throw new Error(
				`[@astrojs/node] Could not find the server directory "${serverFolder}" ` +
					`by walking up from "${import.meta.url}". This can happen when the server ` +
					`entry point is bundled into a single file (e.g. with esbuild) so that ` +
					`import.meta.url no longer contains the original "${serverFolder}" path segment. ` +
					`When bundling the server entry, make sure the output path contains a ` +
					`"${serverFolder}" directory segment, or avoid bundling the server entry entirely.`,
			);
		}
		previous = serverEntryFolderURL;
		serverEntryFolderURL = path.dirname(serverEntryFolderURL);
	}

	// Resolve the client directory by applying the relative path to the runtime server location
	const serverEntryURL = serverEntryFolderURL + '/entry.mjs';
	const clientURL = new URL(appendForwardSlash(rel), serverEntryURL);
	return url.fileURLToPath(clientURL);
}

View on GitHub (pinned to d081033d5f)

Solutions

  1. Stop bundling the @astrojs/node server entry into a single file; deploy `dist/` as a directory tree.
  2. If you must bundle, ensure the output path still contains a `server` directory segment (e.g. `dist/server/entry.js`).
  3. Keep the default `build.server` directory name (`server`) so the basename walk succeeds.

Example fix

// before: esbuild bundles to dist/entry.mjs (no server/ segment)
esbuild dist/server/entry.mjs --bundle --outfile=dist/entry.mjs
// after: preserve the server directory
esbuild dist/server/entry.mjs --bundle --outfile=dist/server/entry.mjs
Defensive patterns

Strategy: validation

Validate before calling

import { fileURLToPath } from 'node:url';
const serverSeg = path.basename(options.server);
if (!fileURLToPath(import.meta.url).includes(path.sep + serverSeg + path.sep)) {
  console.warn('Server entry bundled flat; asset resolution may fail.');
}

Prevention

When it happens

Trigger: Bundling the server entry into a single flat file (esbuild/webpack/ncc) so `import.meta.url` no longer contains a `server/` directory segment. Deploying to a serverless runtime that flattens the output layout. Renaming the build output directory away from `server`.

Common situations: Wrapping the built Node adapter output with esbuild `--bundle`. Using a platform bundler (e.g. Vercel/Netlify functions build) that collapses `dist/server/` into one file. Moving files post-build without preserving the `server` folder name.

Related errors


AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12). Data as JSON: /api/errors/8e0cbddd03bba46a. Report an issue: GitHub.