withastro/astro · error · TypeError

maxDuration must be a positive number

Error message

maxDuration must be a positive number

What it means

The Vercel adapter validates its `maxDuration` option at integration setup, before any build work starts. `maxDuration` maps to the serverless function timeout written to the build output, so Vercel requires a number strictly greater than zero. This specific throw fires when the value is a number but is zero or negative (a preceding check handles non-number values).

Solutions

  1. Set maxDuration to a positive integer (e.g. 10 or 60): `vercel({ maxDuration: 60 })`
  2. Omit maxDuration entirely to use Vercel's default function timeout if you don't need a custom limit
  3. If the value comes from an environment variable, parse and guard it: `const maxDuration = Number(process.env.MAX_DURATION) > 0 ? Number(process.env.MAX_DURATION) : undefined`

Example fix

// before
export default defineConfig({
  adapter: vercel({ maxDuration: 0 }),
});

// after
export default defineConfig({
  adapter: vercel({ maxDuration: 60 }),
});
Defensive patterns

Strategy: validation

Validate before calling

const maxDuration = Number(process.env.VERCEL_MAX_DURATION);
export default defineConfig({
  adapter: vercel({
    maxDuration: Number.isFinite(maxDuration) && maxDuration > 0 ? maxDuration : undefined,
  }),
});

Type guard

function isValidMaxDuration(v: unknown): v is number {
  return typeof v === 'number' && v > 0;
}

Prevention

When it happens

Trigger: Calling `vercel({ maxDuration: 0 })` or `vercel({ maxDuration: -5 })` in astro.config.mjs. It also fires when the value comes from an env var parsed with Number() that resolves to 0 (e.g. `Number('')`), or from a computed expression that clamps to <= 0.

Common situations: Developers set `maxDuration: 0` intending "no limit" instead of omitting the option; config is copied between projects and the value is templated from CI variables that are empty; a preset/shared config object defaults maxDuration to 0.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of withastro/astro@3578d45d34 (2026-08-18). Data as JSON: /api/errors/aa475d6c3388555b. Report an issue: GitHub.

Appendix: source

Thrown at packages/integrations/vercel/src/index.ts:260

	devImageService = 'sharp',
	middlewareMode,
	edgeMiddleware,
	maxDuration,
	isr = false,
	skewProtection = process.env.VERCEL_SKEW_PROTECTION_ENABLED === '1',
	staticHeaders = false,
}: VercelServerlessConfig = {}): AstroIntegration {
	// Resolve middleware mode with backward compatibility
	const resolvedMiddlewareMode = middlewareMode ?? (edgeMiddleware ? 'edge' : 'classic');

	if (maxDuration) {
		if (typeof maxDuration !== 'number') {
			throw new TypeError(`maxDuration must be a number`, {
				cause: maxDuration,
			});
		}
		if (maxDuration <= 0) {
			throw new TypeError(`maxDuration must be a positive number`, {
				cause: maxDuration,
			});
		}
	}

	let _config: AstroConfig;
	let _buildTempFolder: URL;
	let _serverEntry: string;
	let _middlewareEntryPoint: URL | undefined;
	let _hasServerBuild = false;
	let _routeToHeaders: RouteToHeaders | undefined = undefined;
	// Extra files to be merged with `includeFiles` during build
	const extraFilesToInclude: URL[] = [];
	// Secret used to verify that the caller is the astro-generated edge middleware and not a third-party
	const middlewareSecret = crypto.randomUUID();

	let _buildOutput: 'server' | 'static';

View on GitHub (pinned to 3578d45d34)