withastro/astro · error · Error
[@astrojs/node] Could not find the server directory
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
At runtime the Node adapter walks up from import.meta.url (shared.ts) until it finds a directory whose name equals basename(options.server) (the build.server folder, default 'server'); it uses that anchor to compute the relative path from the server bundle to the client assets. If the server entry was re-bundled into a file whose path no longer contains that segment, the walk reaches the filesystem root and throws with this explanation.
Solutions
- Emit the bundle inside the server folder so the path keeps the segment, e.g. dist/server/bundle.mjs
- Avoid re-bundling the server entry entirely and run dist/server/entry.mjs as built
- If you rename the folder, keep a path segment matching basename(config.build.server) in the final output location
Example fix
# before esbuild dist/server/entry.mjs --bundle --outfile=dist/bundle.mjs # after esbuild dist/server/entry.mjs --bundle --outfile=dist/server/bundle.mjs
Defensive patterns
Strategy: validation
Validate before calling
import path from 'node:path';
const serverFolder = path.basename(new URL(config.build.server).pathname); // default 'server'
const outSegments = path.resolve(outfile).split(path.sep);
if (!outSegments.includes(serverFolder)) {
throw new Error(`bundle output must keep a '${serverFolder}' path segment`);
} Prevention
- Emit bundled server output inside dist/server/ so the segment survives
- Smoke-test the bundled entry locally with node before deploying
- If renaming build.server, mirror the rename in the bundler output path
When it happens
Trigger: Post-processing dist/server/entry.mjs with esbuild/webpack/pkg/etc. so the output lands outside a directory named 'server'; copying the bundled entry to another location before running it; renaming build.server without keeping the matching segment in the deployed output path.
Common situations: Deploy pipelines that bundle the server output into a single file at a path like dist/bundle.mjs; Docker images that flatten the build output; custom esbuild steps inherited from other adapters.
Related errors
- GetImageNotUsedOnServer
- <script src=" "> references an asset in the " " directory…
- ServerOnlyModule
- ServerOnlyModule
- Setting the 'mode' option is required.
AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18).
Data as JSON: /api/errors/8e0cbddd03bba46a.
Report an issue: GitHub.
Appendix: 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 52e6c34790)