pnpm/pnpm · error · PnpmError
PACK_APP_INVALID_RUNTIME
PACK_APP_INVALID_RUNTIME
Error message
Invalid runtime "${spec}". Expected format: <name>@<version> (supported runtimes: ${SUPPORTED_RUNTIMES.join(', ')}; e.g. "node@${MIN_BUILDER_VERSION.major}.${MIN_BUILDER_VERSION.minor}.0"). What it means
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.
Source
Thrown at pnpm11/releasing/commands/src/pack-app/packApp.ts:466
// Runtime spec is "<name>@<version>". Only "node" is supported today; the
// prefix is kept so future runtimes (bun, deno) can share the same flag
// without a breaking change. Reading the runtime name rather than a bare
// version also avoids shadowing pnpm's global `node-version` rc setting,
// whose value would otherwise leak into Config['nodeVersion'] and override
// `pnpm.app.runtime`.
const SUPPORTED_RUNTIMES = ['node'] as const
const RUNTIME_PATTERN = /^(node)@(.+)$/
interface ParsedRuntime {
name: typeof SUPPORTED_RUNTIMES[number]
version: string
}
function parseRuntime (spec: string): ParsedRuntime {
const match = RUNTIME_PATTERN.exec(spec)
if (!match) {
throw new PnpmError('PACK_APP_INVALID_RUNTIME',
`Invalid runtime "${spec}". Expected format: <name>@<version> (supported runtimes: ${SUPPORTED_RUNTIMES.join(', ')}; e.g. "node@${MIN_BUILDER_VERSION.major}.${MIN_BUILDER_VERSION.minor}.0").`)
}
return { name: match[1] as ParsedRuntime['name'], version: match[2] }
}
// Characters that Win32 rejects in filenames, plus NUL. Path separators are
// checked separately via `path.basename` so the message is crisp.
const INVALID_FILENAME_CHARS = /[<>:"|?*\0]/
// Win32 reserved device names (case-insensitive, with or without an extension).
const RESERVED_WINDOWS_NAME = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i
// Reject anything that would let the output escape its target directory, or
// that would fail filesystem-level validation on any supported host. This
// surfaces problems at `pack-app` invocation time instead of letting them
// blow up later in `writeFile(outputFile, …)`.
function validateOutputName (name: string): string {
if (
name !== path.basename(name) ||View on GitHub (pinned to 6261b7f388)
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.
Example fix
# before
pnpm pack-app --runtime 25.5.0
# after
pnpm pack-app --runtime node@25.5.0
# package.json
"pnpm": { "app": { "runtime": "node@25.5.0" } } Defensive patterns
Strategy: validation
Validate before calling
const RUNTIME_PATTERN = /^(node)@(.+)$/
function isValidRuntimeSpec(spec: string): boolean {
return RUNTIME_PATTERN.test(spec)
}
function toRuntimeSpec(v: string): string {
return v.includes('@') ? v : `node@${v}` // repair bare versions
} Type guard
function isNodeRuntimeSpec(spec: string): spec is `node@${string}` {
return /^node@(.+)$/.test(spec)
} Try / catch
try {
await runPackApp(opts)
} catch (err) {
if ((err as any)?.code === 'PACK_APP_INVALID_RUNTIME') {
await runPackApp({ ...opts, runtime: 'node@25.5.0' })
} else throw err
} Prevention
- 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.
When it happens
Trigger: `--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.
Common situations: 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.
Related errors
- INVALID_NODE_RELEASE_CHANNEL
- PACK_APP_INVALID_TARGET
- PACK_APP_INVALID_OUTPUT_NAME
- BUN_RESOLUTION_FAILURE
- RUNTIME_NO_SUBCOMMAND
AI-assisted analysis of pnpm/pnpm@6261b7f388 (2026-08-17).
Data as JSON: /api/errors/763dd916aea76a3a.
Report an issue: GitHub.