{"record":{"id":"758b04c57e3fda9e","repo":"pnpm/pnpm","slug":"pack-app-invalid-target","errorCode":"PACK_APP_INVALID_TARGET","errorMessage":"Invalid target: \"${raw}\". Expected format: <os>-<arch>[-<libc>] where <os> is ${SUPPORTED_OS.join('|')}, <arch> is x64|arm64, optional <libc> is musl (linux only).","messagePattern":"Invalid target: \"(.+?)\"\\. Expected format: <os>-<arch>\\[-<libc>\\] where <os> is (.+?), <arch> is x64\\|arm64, optional <libc> is musl \\(linux only\\)\\.","errorType":"exception","errorClass":"PnpmError","httpStatus":null,"severity":"error","filePath":"pnpm11/releasing/commands/src/pack-app/packApp.ts","lineNumber":438,"sourceCode":"  const nodeMirrorBaseUrl = getNodeMirror(nodeDownloadMirrors, releaseChannel)\n  const version = await resolveNodeVersion(fetch, versionSpecifier, nodeMirrorBaseUrl)\n  if (!version) {\n    throw new PnpmError('PACK_APP_NODE_VERSION_NOT_FOUND',\n      `Could not find a Node.js version that satisfies \"${specifier}\"`)\n  }\n  return version\n}\n\n// Parsed triplet must match this shape exactly. We anchor and constrain each\n// segment so that inputs like `linux-x64-musl-../../outside` are rejected\n// outright — otherwise `target.raw` would later flow into path.join for the\n// output directory and could escape it.\nconst TARGET_PATTERN = /^(linux|darwin|win32)-(x64|arm64)(?:-(musl))?$/\n\nfunction parseTarget (raw: string): ParsedTarget {\n  const match = TARGET_PATTERN.exec(raw)\n  if (!match) {\n    throw new PnpmError('PACK_APP_INVALID_TARGET',\n      `Invalid target: \"${raw}\". Expected format: <os>-<arch>[-<libc>] where <os> is ${SUPPORTED_OS.join('|')}, <arch> is x64|arm64, optional <libc> is musl (linux only).`)\n  }\n  const [, platform, arch, libc] = match\n  if (libc === 'musl' && platform !== 'linux') {\n    throw new PnpmError('PACK_APP_INVALID_TARGET',\n      `The \"musl\" libc suffix is only valid for linux targets (got \"${raw}\").`)\n  }\n  return { raw, platform, arch, libc: libc || undefined }\n}\n\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)@(.+)$/","sourceCodeStart":420,"sourceCodeEnd":456,"githubUrl":"https://github.com/pnpm/pnpm/blob/6261b7f388016d57ca6b90340342411cd1d0d00f/pnpm11/releasing/commands/src/pack-app/packApp.ts#L420-L456","documentation":"pack-app --target takes a strict triplet `<os>-<arch>[-<libc>]` validated by the anchored TARGET_PATTERN /^(linux|darwin|win32)-(x64|arm64)(?:-(musl))?$/. Anything else — different OS names (freebsd, sunos), arch names (x86_64, aarch64, i686, armv7), wrong separators, extra segments, or traversal payloads like `linux-x64-musl-../../outside` — fails to match and is rejected immediately. The strictness is deliberate: the raw string later flows into path.join for the output directory, so unanchored parsing would allow escaping the output directory.","triggerScenarios":"Passing `--target x86_64-linux` (uname-style, wrong order and name), `--target linux-armv7l`, `--target darwin-x64-gnu`, `--target Linux-x64` (uppercase), or any string with path segments. Any of these produces this error before any network or build work happens.","commonSituations":"Copy-pasting targets from Docker/Rust/Go toolchains that use `x86_64-unknown-linux-gnu` style triplets; assuming pnpm accepts uname(1) output; scripting pack-app from `process.platform`-adjacent values that use different naming (e.g. 'win32' vs 'windows').","solutions":["Use exactly one of linux|darwin|win32 for the OS and x64|arm64 for the arch, e.g. `--target linux-x64`, `--target darwin-arm64`, `--target win32-x64`.","Add the optional `-musl` suffix only for Linux Alpine-style targets: `--target linux-x64-musl`.","If you generate targets in a script, normalize `x86_64`→x64, `aarch64`/`arm64e`→arm64, `windows`→win32, `macos`/`osx`→darwin before invoking pack-app."],"exampleFix":"# before\npnpm pack-app --target x86_64-unknown-linux-gnu\n\n# after\npnpm pack-app --target linux-x64\n# or for Alpine\npnpm pack-app --target linux-x64-musl","handlingStrategy":"validation","validationCode":"const TARGET_PATTERN = /^(linux|darwin|win32)-(x64|arm64)(?:-(musl))?$/\nfunction isValidTarget(raw: string): boolean {\n  return TARGET_PATTERN.test(raw)\n}\n\nconst NORMALIZE: Record<string, string> = {\n  x86_64: 'x64', aarch64: 'arm64', arm64e: 'arm64',\n  windows: 'win32', macos: 'darwin', osx: 'darwin',\n}\nfunction normalizeTarget(raw: string): string {\n  let [os, arch, libc] = raw.split('-')\n  os = NORMALIZE[os] ?? os\n  arch = NORMALIZE[arch] ?? arch\n  return libc ? `${os}-${arch}-${libc}` : `${os}-${arch}`\n}","typeGuard":"function isPackAppTarget(raw: string): boolean {\n  return /^(linux|darwin|win32)-(x64|arm64)(?:-(musl))?$/.test(raw)\n}","tryCatchPattern":"try {\n  await runPackApp({ ...opts, targets })\n} catch (err) {\n  if ((err as any)?.code === 'PACK_APP_INVALID_TARGET') {\n    targets = targets.map(normalizeTarget).filter(isPackAppTarget)\n    await runPackApp({ ...opts, targets })\n  } else throw err\n}","preventionTips":["Never pass uname(1) or Rust/Go triplets through unchecked — normalize x86_64/aarch64 to x64/arm64 first.","Validate targets against the anchored regex in your build script before invoking pnpm.","Build target lists from a fixed allowlist (6 Linux/macOS/Windows combos plus linux musl variants), not from user input."],"tags":["pack-app","target","cli-validation","cross-compile"],"backgroundTag":"invalid-build-target","analyzedSha":"6261b7f388016d57ca6b90340342411cd1d0d00f","analyzedAt":"2026-08-17T18:30:54.750Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}