{"record":{"id":"6a6bcb0a11463052","repo":"abhigyanpatwari/GitNexus","slug":"python-workspace-extractor-duplicate-package","errorCode":null,"errorMessage":"[python-workspace-extractor] duplicate package \"${manifest.name}\" in \"${groupPath}\" and \"${existing.groupPath}\" — skipping \"${groupPath}\"","messagePattern":"\\[python-workspace-extractor\\] duplicate package \"(.+?)\" in \"(.+?)\" and \"(.+?)\" — skipping \"(.+?)\"","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"gitnexus/src/core/group/extractors/python-workspace-extractor.ts","lineNumber":214,"sourceCode":"  const packagesByGroupPath = new Map<string, PythonPackageMeta>();\n\n  for (const [groupPath] of Object.entries(repos)) {\n    const repoPath = repoPaths.get(groupPath);\n    if (!repoPath) continue;\n\n    const manifest = await parsePythonManifest(repoPath);\n    if (!manifest) continue;\n\n    const meta: PythonPackageMeta = {\n      name: manifest.name,\n      importName: manifest.importName,\n      groupPath,\n      repoPath,\n      workspaceDeps: manifest.deps,\n    };\n    const existing = packagesByImportName.get(manifest.importName);\n    if (existing) {\n      logger.warn(\n        `[python-workspace-extractor] duplicate package \"${manifest.name}\" in \"${groupPath}\" and \"${existing.groupPath}\" — skipping \"${groupPath}\"`,\n      );\n      continue;\n    }\n    packagesByImportName.set(manifest.importName, 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 normalizedDeps = pkg.workspaceDeps.map((d) => d.replace(/-/g, '_'));\n    const groupPkgDeps = normalizedDeps.filter((d) => packagesByImportName.has(d));\n    if (groupPkgDeps.length === 0) continue;\n\n    const knownPackages = new Map<string, string>();\n    for (const dep of groupPkgDeps) {","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/52924ef12c2290ceee4612526a828ec4cdf2047f/gitnexus/src/core/group/extractors/python-workspace-extractor.ts#L196-L232","documentation":"Emitted by the group Python workspace extractor. Two scanned directories produced manifests whose import name (importName) is identical; packages are keyed by importName in a packagesByImportName map, so the later registration (groupPath) is skipped and its workspace dependency links are never built. Cross-repo resolution for the skipped package degrades after this warning.","triggerScenarios":"A group contains two pyproject.toml/setup.py declaring the same project or import name — a library vendored into a second repo, or two packages whose top-level module directory resolves to the same import name.","commonSituations":"Forked Python packages kept in one group; duplicated internal utility libraries; src-layout packages that both expose the same top-level module name.","solutions":["Rename the duplicate package's project name and top-level import package so importName is unique","Ignore or remove the vendored duplicate so the extractor does not scan it","Re-run group analyze and verify the warning disappears and workspace links resolve"],"exampleFix":"# before — repo-b/pyproject.toml\n[project]\nname = \"payments-lib\"\n\n# after\n[project]\nname = \"payments-lib-v2\"  # rename the import package dir to match","handlingStrategy":"validation","validationCode":"import { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nfunction assertUniquePythonImports(repoDirs: string[]): void {\n  const seen = new Map<string, string>();\n  for (const dir of repoDirs) {\n    let name: string | undefined;\n    try {\n      const raw = JSON.parse(readFileSync(join(dir, 'pyproject.json-dump'), 'utf8')); // or parse TOML\n      name = raw?.project?.name ?? raw?.tool?.poetry?.name;\n    } catch {\n      continue;\n    }\n    if (!name) continue;\n    const importName = name.replaceAll('-', '_').replaceAll('.', '_');\n    if (seen.has(importName)) {\n      throw new Error(`duplicate import \"${importName}\" in ${seen.get(importName)} and ${dir}`);\n    }\n    seen.set(importName, dir);\n  }\n}","typeGuard":"function hasUniqueImports<T extends { importName: string }>(items: readonly T[]): boolean {\n  return new Set(items.map((i) => i.importName)).size === items.length;\n}","tryCatchPattern":null,"preventionTips":["Give every Python package in a group a unique project name and top-level module directory","Do not vendor a package copy into a sibling group repo; reference it via the group link manifest instead","Fail CI on duplicate project names across group repos before running group sync"],"tags":["python","workspace","pyproject","monorepo","duplicate-name"],"backgroundTag":"duplicate-package-name","analyzedSha":"52924ef12c2290ceee4612526a828ec4cdf2047f","analyzedAt":"2026-08-20T23:29:22.980Z","contentChangedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}