withastro/astro · error · Error

[preview] cannot preview your app.

Error message

[preview] ${settings.adapter.name} cannot preview your app.

What it means

Preview resolves the adapter's `previewEntrypoint` with `require.resolve()` relative to the project root (so package managers like pnpm do not hoist it into Astro), imports it as a module, and calls its default export to obtain the server. If that module's default export is anything other than a function, the adapter contract is broken — usually a malformed custom adapter, a broken package publish, or a version mismatch — and preview aborts.

Solutions

  1. Reinstall dependencies (`rm -rf node_modules && npm install` or `pnpm install`) to rule out a corrupted package
  2. Upgrade the adapter to a version compatible with your Astro version
  3. If you maintain the adapter, make the preview entrypoint export a default async function receiving the preview options and returning a server object
  4. Check the adapter package's `exports` map actually points `previewEntrypoint` at the file you think it does

Example fix

// before — adapter preview entrypoint
export const preview = async (options) => { /* start server */ };

// after
export default async function preview(options) {
  const server = http.createServer(/* serve files from options.server outDir */);
  await new Promise((r) => server.listen(options.port, options.host, r));
  return { server, closed: false };
}
Defensive patterns

Strategy: type-guard

Validate before calling

// adapter smoke test — import the preview entrypoint the way preview does
import { pathToFileURL } from 'node:url';
import { createRequire } from 'node:module';
const req = createRequire(process.cwd() + '/');
const mod = await import(pathToFileURL(req.resolve('./my-adapter/preview.js')).href);
if (typeof mod.default !== 'function') throw new Error('adapter preview entrypoint malformed');

Type guard

// Narrow an imported adapter preview module to a callable server factory
export function isPreviewModule(m: unknown): m is { default: (opts: any) => Promise<any> } {
  return typeof (m as any)?.default === 'function';
}

Prevention

When it happens

Trigger: A custom adapter whose preview entrypoint uses a named export (`export const preview`) or an object instead of `export default`; a partially published or corrupted adapter package where the entrypoint resolves to the wrong file (exports-map mismatch); an adapter version whose preview API predates the current Astro runtime.

Common situations: Authoring a first-party adapter and forgetting the default-export convention; mixing an adapter built for an older Astro with a newer core; a bad install (half-written files) after an interrupted npm/pnpm install.

Related errors


AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18). Data as JSON: /api/errors/395ef1ed35e9b35e. Report an issue: GitHub.

Appendix: source

Thrown at packages/astro/src/core/preview/index.ts:81

		throw new Error(`[preview] No adapter found.`);
	}

	if (!settings.adapter.previewEntrypoint) {
		throw new Error(
			`[preview] The ${settings.adapter.name} adapter does not support the preview command.`,
		);
	}
	// We need to use require.resolve() here so that advanced package managers like pnpm
	// don't treat this as a dependency of Astro itself. This correctly resolves the
	// preview entrypoint of the integration package, relative to the user's project root.
	const require = createRequire(settings.config.root);
	const previewEntrypointUrl = pathToFileURL(
		require.resolve(settings.adapter.previewEntrypoint.toString()),
	).href;

	const previewModule = (await import(previewEntrypointUrl)) as Partial<PreviewModule>;
	if (typeof previewModule.default !== 'function') {
		throw new Error(`[preview] ${settings.adapter.name} cannot preview your app.`);
	}

	const server = await previewModule.default({
		outDir: settings.config.outDir,
		client: settings.config.build.client,
		server: settings.config.build.server,
		serverEntrypoint: new URL(settings.config.build.serverEntry, settings.config.build.server),
		host: getResolvedHostForHttpServer(settings.config.server.host),
		port: settings.config.server.port,
		base: settings.config.base,
		logger: new AstroIntegrationLogger(logger.options, settings.adapter.name),
		headers: settings.config.server.headers,
		allowedHosts: settings.config.server.allowedHosts,
		open: settings.config.server.open,
		root: settings.config.root,
	});

	return server;

View on GitHub (pinned to 52e6c34790)