{"record":{"id":"7d7c7930f46ed9c4","repo":"withastro/astro","slug":"maxduration-must-be-a-number","errorCode":null,"errorMessage":"maxDuration must be a number","messagePattern":"maxDuration must be a number","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/integrations/vercel/src/index.ts","lineNumber":254,"sourceCode":"\twebAnalytics,\n\tincludeFiles: _includeFiles = [],\n\texcludeFiles: _excludeFiles = [],\n\timageService,\n\timagesConfig,\n\tdevImageService = 'sharp',\n\tmiddlewareMode,\n\tedgeMiddleware,\n\tmaxDuration,\n\tisr = false,\n\tskewProtection = process.env.VERCEL_SKEW_PROTECTION_ENABLED === '1',\n\tstaticHeaders = false,\n}: VercelServerlessConfig = {}): AstroIntegration {\n\t// Resolve middleware mode with backward compatibility\n\tconst resolvedMiddlewareMode = middlewareMode ?? (edgeMiddleware ? 'edge' : 'classic');\n\n\tif (maxDuration) {\n\t\tif (typeof maxDuration !== 'number') {\n\t\t\tthrow new TypeError(`maxDuration must be a number`, {\n\t\t\t\tcause: maxDuration,\n\t\t\t});\n\t\t}\n\t\tif (maxDuration <= 0) {\n\t\t\tthrow new TypeError(`maxDuration must be a positive number`, {\n\t\t\t\tcause: maxDuration,\n\t\t\t});\n\t\t}\n\t}\n\n\tlet _config: AstroConfig;\n\tlet _buildTempFolder: URL;\n\tlet _serverEntry: string;\n\tlet _middlewareEntryPoint: URL | undefined;\n\tlet _routeToHeaders: RouteToHeaders | undefined = undefined;\n\t// Extra files to be merged with `includeFiles` during build\n\tconst extraFilesToInclude: URL[] = [];\n\t// Secret used to verify that the caller is the astro-generated edge middleware and not a third-party","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/withastro/astro/blob/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/integrations/vercel/src/index.ts#L236-L272","documentation":"Thrown by the Vercel serverless adapter when `vercel({ serverless: { maxDuration } })` is truthy but not a number type. `maxDuration` maps to Vercel's function timeout and must be numeric to be serialized into the function config.","triggerScenarios":"Passing `maxDuration` as a string (e.g. from an env var read without `Number()`), an object, or an array. The guard `if (maxDuration)` passes for any truthy value, then `typeof maxDuration !== 'number'` fires.","commonSituations":"Reading `maxDuration` from `process.env.MAX_DURATION` (always a string) without coercion. Spreading a JSON config where the value is quoted.","solutions":["Pass a numeric literal: `vercel({ serverless: { maxDuration: 60 } })`.","Coerce env-var inputs: `maxDuration: Number(process.env.MAX_DURATION)`.","Validate the value type before assigning it to the config object."],"exampleFix":"// before\nexport default defineConfig({\n  adapter: vercel({ serverless: { maxDuration: process.env.MAX_DURATION } }),\n});\n// after\nexport default defineConfig({\n  adapter: vercel({\n    serverless: { maxDuration: Number(process.env.MAX_DURATION) },\n  }),\n});","handlingStrategy":"type-guard","validationCode":"function asMaxDuration(v: unknown): number | undefined {\n  if (v == null) return undefined;\n  const n = Number(v);\n  if (!Number.isFinite(n)) throw new TypeError('maxDuration must be a number');\n  return n;\n}\n// usage: vercel({ serverless: { maxDuration: asMaxDuration(process.env.MAX_DURATION) } })","typeGuard":"function isNumberLike(v: unknown): v is number | string {\n  return typeof v === 'number' || (typeof v === 'string' && v.trim() !== '' && !isNaN(Number(v)));\n}","tryCatchPattern":null,"preventionTips":["Always coerce env-var-sourced numbers with `Number()`.","Type the config builder input strictly (no `any`).","Add a config sanity check that logs resolved numeric options."],"tags":["vercel","serverless","config","type-validation"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}