{"record":{"id":"763dd916aea76a3a","repo":"pnpm/pnpm","slug":"pack-app-invalid-runtime","errorCode":"PACK_APP_INVALID_RUNTIME","errorMessage":"Invalid runtime \"${spec}\". Expected format: <name>@<version> (supported runtimes: ${SUPPORTED_RUNTIMES.join(', ')}; e.g. \"node@${MIN_BUILDER_VERSION.major}.${MIN_BUILDER_VERSION.minor}.0\").","messagePattern":"Invalid runtime \"(.+?)\"\\. Expected format: <name>@<version> \\(supported runtimes: (.+?); e\\.g\\. \"node@(.+?)\\.(.+?)\\.0\"\\)\\.","errorType":"exception","errorClass":"PnpmError","httpStatus":null,"severity":"error","filePath":"pnpm11/releasing/commands/src/pack-app/packApp.ts","lineNumber":466,"sourceCode":"\n// Runtime spec is \"<name>@<version>\". Only \"node\" is supported today; the\n// prefix is kept so future runtimes (bun, deno) can share the same flag\n// without a breaking change. Reading the runtime name rather than a bare\n// version also avoids shadowing pnpm's global `node-version` rc setting,\n// whose value would otherwise leak into Config['nodeVersion'] and override\n// `pnpm.app.runtime`.\nconst SUPPORTED_RUNTIMES = ['node'] as const\nconst RUNTIME_PATTERN = /^(node)@(.+)$/\n\ninterface ParsedRuntime {\n  name: typeof SUPPORTED_RUNTIMES[number]\n  version: string\n}\n\nfunction parseRuntime (spec: string): ParsedRuntime {\n  const match = RUNTIME_PATTERN.exec(spec)\n  if (!match) {\n    throw new PnpmError('PACK_APP_INVALID_RUNTIME',\n      `Invalid runtime \"${spec}\". Expected format: <name>@<version> (supported runtimes: ${SUPPORTED_RUNTIMES.join(', ')}; e.g. \"node@${MIN_BUILDER_VERSION.major}.${MIN_BUILDER_VERSION.minor}.0\").`)\n  }\n  return { name: match[1] as ParsedRuntime['name'], version: match[2] }\n}\n\n// Characters that Win32 rejects in filenames, plus NUL. Path separators are\n// checked separately via `path.basename` so the message is crisp.\nconst INVALID_FILENAME_CHARS = /[<>:\"|?*\\0]/\n// Win32 reserved device names (case-insensitive, with or without an extension).\nconst RESERVED_WINDOWS_NAME = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\\..*)?$/i\n\n// Reject anything that would let the output escape its target directory, or\n// that would fail filesystem-level validation on any supported host. This\n// surfaces problems at `pack-app` invocation time instead of letting them\n// blow up later in `writeFile(outputFile, …)`.\nfunction validateOutputName (name: string): string {\n  if (\n    name !== path.basename(name) ||","sourceCodeStart":448,"sourceCodeEnd":484,"githubUrl":"https://github.com/pnpm/pnpm/blob/6261b7f388016d57ca6b90340342411cd1d0d00f/pnpm11/releasing/commands/src/pack-app/packApp.ts#L448-L484","documentation":"The runtime spec must match /^(node)@(.+)$/ — a runtime name, an @, and a version. Only the name \"node\" is supported today; the name@version prefix is kept so future runtimes (bun, deno) can reuse the flag without a breaking change. Parsing the name also avoids pnpm's global `node-version` rc setting leaking into Config['nodeVersion'] and shadowing `pnpm.app.runtime`. A bare version (\"25.5.0\"), a missing version (\"node@\"), \"node\" alone, or another runtime name (\"bun@1\") all fail the pattern.","triggerScenarios":"`--runtime 25.5.0` (bare version, no name), `--runtime node` (no @version), `--runtime bun@1.0.0` or `--runtime deno@2` (unsupported name), `\"pnpm.app.runtime\": \"v25.5.0\"` with a leading v and no name. Note this error fires on the *shape*, before any version resolution — a well-formed node@<old-version> instead raises PACK_APP_RUNTIME_TOO_OLD.","commonSituations":"Users assuming --runtime takes the same value as `pnpm env use` (bare versions); copying `engines.node` values into pnpm.app.runtime; early adopters trying bun/deno SEAs after seeing the flag.","solutions":["Write the spec as name@version: `--runtime node@25.5.0` (a major-only form like node@25 also matches the pattern).","In package.json: `\"pnpm\": { \"app\": { \"runtime\": \"node@25.5.0\" } }`.","If you intended another runtime, know that only node is supported today — build a plain SEA with that runtime's own tooling instead."],"exampleFix":"# before\npnpm pack-app --runtime 25.5.0\n\n# after\npnpm pack-app --runtime node@25.5.0\n\n# package.json\n\"pnpm\": { \"app\": { \"runtime\": \"node@25.5.0\" } }","handlingStrategy":"validation","validationCode":"const RUNTIME_PATTERN = /^(node)@(.+)$/\nfunction isValidRuntimeSpec(spec: string): boolean {\n  return RUNTIME_PATTERN.test(spec)\n}\n\nfunction toRuntimeSpec(v: string): string {\n  return v.includes('@') ? v : `node@${v}` // repair bare versions\n}","typeGuard":"function isNodeRuntimeSpec(spec: string): spec is `node@${string}` {\n  return /^node@(.+)$/.test(spec)\n}","tryCatchPattern":"try {\n  await runPackApp(opts)\n} catch (err) {\n  if ((err as any)?.code === 'PACK_APP_INVALID_RUNTIME') {\n    await runPackApp({ ...opts, runtime: 'node@25.5.0' })\n  } else throw err\n}","preventionTips":["Always write runtime specs as name@version, including the node@ prefix, in both flags and pnpm.app.runtime.","Normalize user/env-provided values with toRuntimeSpec before passing them to pack-app.","Remember only node is supported today — reject bun/deno specs early in your own tooling."],"tags":["pack-app","runtime","cli-validation","specifier"],"backgroundTag":"invalid-runtime-specifier","analyzedSha":"6261b7f388016d57ca6b90340342411cd1d0d00f","analyzedAt":"2026-08-17T18:30:54.750Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}