{"record":{"id":"9a16f8589f28e795","repo":"abhigyanpatwari/GitNexus","slug":"node-workspace-extractor-duplicate-package-name","errorCode":null,"errorMessage":"[node-workspace-extractor] duplicate package name \"${manifest.name}\" in \"${groupPath}\" and \"${existing.groupPath}\" — skipping \"${groupPath}\"","messagePattern":"\\[node-workspace-extractor\\] duplicate package name \"(.+?)\" in \"(.+?)\" and \"(.+?)\" — skipping \"(.+?)\"","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"gitnexus/src/core/group/extractors/node-workspace-extractor.ts","lineNumber":209,"sourceCode":"  const packagesByName = new Map<string, PackageMeta>();\n  const packagesByGroupPath = new Map<string, PackageMeta>();\n\n  for (const [groupPath] of Object.entries(repos)) {\n    const repoPath = repoPaths.get(groupPath);\n    if (!repoPath) continue;\n\n    const manifest = await parsePackageManifest(repoPath);\n    if (!manifest) continue;\n\n    const meta: PackageMeta = {\n      name: manifest.name,\n      groupPath,\n      repoPath,\n      workspaceDeps: manifest.workspaceDeps,\n    };\n    const existing = packagesByName.get(manifest.name);\n    if (existing) {\n      logger.warn(\n        `[node-workspace-extractor] duplicate package name \"${manifest.name}\" in \"${groupPath}\" and \"${existing.groupPath}\" — skipping \"${groupPath}\"`,\n      );\n      continue;\n    }\n    packagesByName.set(manifest.name, meta);\n    packagesByGroupPath.set(groupPath, meta);\n  }\n\n  const links: GroupManifestLink[] = [];\n  const seen = new Set<string>();\n\n  for (const [, pkg] of packagesByGroupPath) {\n    const groupPkgDeps = pkg.workspaceDeps.filter((d) => packagesByName.has(d));\n    if (groupPkgDeps.length === 0) continue;\n\n    const knownPackages = new Set(groupPkgDeps);\n    const imports = await scanImports(pkg.repoPath, knownPackages);\n","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/core/group/extractors/node-workspace-extractor.ts#L191-L227","documentation":"Emitted by the group Node workspace extractor while registering workspace packages across a group's repos. Two scanned directories produced package.json manifests with the identical \"name\" field; since packages are keyed by name in a packagesByName map, the later registration (groupPath) is dropped and only the first wins. The skipped package contributes no workspace dependency links, so cross-repo resolution for it silently degrades after this warning.","triggerScenarios":"Running group analyze/sync on a group where two workspace members (or two group repos) both declare e.g. \"name\": \"api-lib\" in package.json: a vendored copy of a package, a fork that kept the original name, or a template package instantiated twice.","commonSituations":"Monorepos with vendored or example packages duplicating a real package's name; groups containing both a library and its vendored copy; renamed packages where one directory still carries the old package.json name.","solutions":["Rename the duplicate package.json \"name\" to a unique value (e.g. scope it: \"@org/api-lib\") so both packages register","If one copy is vendored or generated, add it to the repo's ignore rules so the extractor never scans it","Remove or merge the stale fork carrying the duplicate name","Re-run group analyze and confirm the warning is gone and workspace links resolve"],"exampleFix":"// before — packages/a/package.json\n{ \"name\": \"api-lib\", \"version\": \"1.0.0\" }\n// packages/b/package.json\n{ \"name\": \"api-lib\", \"version\": \"2.0.0\" }\n\n// after — packages/b/package.json\n{ \"name\": \"@org/api-lib-v2\", \"version\": \"2.0.0\" }","handlingStrategy":"validation","validationCode":"import { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nfunction assertUniquePackageNames(repoDirs: string[]): void {\n  const seen = new Map<string, string>();\n  for (const dir of repoDirs) {\n    let name: string | undefined;\n    try {\n      name = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')).name;\n    } catch {\n      continue; // no manifest — the extractor skips it too\n    }\n    if (!name) continue;\n    if (seen.has(name)) {\n      throw new Error(`duplicate package name \"${name}\" in ${seen.get(name)} and ${dir}`);\n    }\n    seen.set(name, dir);\n  }\n}\n// run before group sync\nassertUniquePackageNames(groupRepoDirs);","typeGuard":"function hasUniqueNames<T extends { name: string }>(items: readonly T[]): boolean {\n  return new Set(items.map((i) => i.name)).size === items.length;\n}","tryCatchPattern":null,"preventionTips":["Keep package.json names unique across the whole group; use npm scopes for intentionally same-named packages","Keep vendored/example packages out of the scanned tree via ignore rules","Add a CI check that fails when two package.json files in the group share a name"],"tags":["node","workspace","package-json","monorepo","duplicate-name"],"backgroundTag":"duplicate-package-name","analyzedSha":"aac7515d2a8c50a1f8f923c6fb77218b333560d6","analyzedAt":"2026-08-20T23:29:22.980Z","contentChangedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}