{"record":{"id":"47a422b3b56b6911","repo":"pnpm/pnpm","slug":"invalid-package-name","errorCode":"INVALID_PACKAGE_NAME","errorMessage":"Package in ${target} must have a name to get bin linked.","messagePattern":"Package in (.+?) must have a name to get bin linked\\.","errorType":"error_code","errorClass":"PnpmError","httpStatus":null,"severity":"error","filePath":"pnpm11/bins/linker/src/index.ts","lineNumber":220,"sourceCode":"  },\n  target: string\n): Promise<CommandInfo[]> {\n  const manifest = opts.allowExoticManifests\n    ? (await safeReadProjectManifestOnly(target) as DependencyManifest)\n    : await safeReadPkgJson(target)\n\n  if (manifest == null) {\n    // There's a directory in node_modules without package.json: ${target}.\n    // This used to be a warning but it didn't really cause any issues.\n    return []\n  }\n\n  if (isEmpty(manifest.bin) && !await isFromModules(target)) {\n    opts.warn(`Package in ${target} must have a non-empty bin field to get bin linked.`, 'EMPTY_BIN')\n  }\n\n  if (typeof manifest.bin === 'string' && !manifest.name) {\n    throw new PnpmError('INVALID_PACKAGE_NAME', `Package in ${target} must have a name to get bin linked.`)\n  }\n\n  return getPackageBinsFromManifest(manifest, target)\n}\n\nasync function getPackageBinsFromManifest (manifest: DependencyManifest, pkgDir: string): Promise<CommandInfo[]> {\n  const cmds = await getBinsFromPackageManifest(manifest, pkgDir)\n  let nodeExecPath: string | undefined\n  if (manifest.engines?.runtime && runtimeHasNodeDownloaded(manifest.engines.runtime)) {\n    const require = createRequire(import.meta.dirname)\n    // Using Node.js’ resolution algorithm is the most reliable way to find the Node.js\n    // package that comes from this CLI's dependencies, because the layout of node_modules can vary.\n    // In an isolated layout, it will be located in the same node_modules directory as the CLI.\n    // In a hoisted layout, it may be in one of the parent node_modules directories.\n    const nodeDir = path.dirname(require.resolve('node/CHANGELOG.md', { paths: [pkgDir] }))\n    if (nodeDir) {\n      nodeExecPath = path.join(nodeDir, IS_WINDOWS ? 'node.exe' : 'bin/node')\n    }","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/pnpm/pnpm/blob/5b11d3a15b9022a2109cb18ed96a5d652630371f/pnpm11/bins/linker/src/index.ts#L202-L238","documentation":"When linking bins for a package under node_modules, the linker accepts `bin` as an object (`{ 'cmd': 'file.js' }`) or as a string shorthand whose command name is derived from `manifest.name`. If `bin` is a string but the manifest has no `name`, the command name cannot be derived, so it throws INVALID_PACKAGE_NAME.","triggerScenarios":"`pnpm install`/`pnpm link` processing a dependency (often a git dep, `file:` dep, or symlinked local package) whose package.json has `\"bin\": \"./cli.js\"` but no `name` field.","commonSituations":"Local scaffolding packages or test fixtures written without a name; generated manifests; git dependencies with minimal package.json; linking a work-in-progress package before its manifest is finalized.","solutions":["Add `\"name\": \"your-pkg\"` to the package.json at the path shown in the error","Or switch `bin` to the explicit object form: `\"bin\": { \"your-cmd\": \"./cli.js\" }`, which does not depend on `name`","Re-run the install/link after saving the manifest"],"exampleFix":"// before (package.json)\n{\n  \"version\": \"0.1.0\",\n  \"bin\": \"./cli.js\"\n}\n\n// after\n{\n  \"name\": \"my-cli\",\n  \"version\": \"0.1.0\",\n  \"bin\": \"./cli.js\"\n}\n// alternative: \"bin\": { \"my-cli\": \"./cli.js\" }","handlingStrategy":"type-guard","validationCode":"const manifest = JSON.parse(await fs.readFile(pkgJsonPath, 'utf8'))\nif (typeof manifest.bin === 'string' && !manifest.name) {\n  throw new Error(`${pkgJsonPath}: string \"bin\" requires a \"name\" — fix before install`)\n}","typeGuard":"interface BinManifest { name?: string, bin?: unknown }\n\nfunction hasLinkableBin (m: BinManifest): boolean {\n  if (m.bin == null) return false\n  if (typeof m.bin === 'object') return Object.keys(m.bin).length > 0\n  // string shorthand: only linkable when a name exists to derive the command from\n  return typeof m.name === 'string' && m.name.length > 0\n}","tryCatchPattern":null,"preventionTips":["Always set `name` in package.json for any package that declares bins","Prefer the object form of `bin` (`{ 'cmd': 'file.js' }`) so linking never depends on `name`","Validate fixtures and generated manifests with a lint step before they reach install"],"tags":["bins","manifest","package-json","install"],"backgroundTag":null,"analyzedSha":"5b11d3a15b9022a2109cb18ed96a5d652630371f","analyzedAt":"2026-08-16T13:18:59.198Z","schemaVersion":2},"datasetVersion":"2026-08-16T18:17:16.020Z"}