abhigyanpatwari/GitNexus · warning

[node] package : candidate source entries ( ) — none…

Error message

[node] package ${name}: ${discovered.ambiguous.length} candidate source entries (${discovered.ambiguous.join(', ')}) — none adopted; declare `source` or a single lib entry

What it means

This is a logged warning (not a thrown error) emitted while reading a workspace package's manifest. GitNexus tries to infer a package's source entry point for import resolution; when the package has multiple ambiguous candidate entries (e.g. both `src/index` and `lib/index` exist, or several discovery sources produced candidates) it refuses to guess, adopts none, and warns. Imports into that package may then resolve less precisely until the package declares an explicit entry.

Solutions

  1. Add an explicit `source` field to the package's package.json pointing at the canonical entry (e.g. "source": "./src/index.ts").
  2. Declare `exports` in package.json so the resolver has an unambiguous entry map instead of falling back to conventional discovery.
  3. Remove or rename the extra candidate entry (e.g. delete a stale duplicate `index.ts` at the package root or an obsolete `lib/index`) so only one candidate remains.
  4. If the ambiguity is intentional (multi-entry package), accept the warning; resolution will fall back to other heuristics for that package.

Example fix

// before: package.json with ambiguous entries on disk (src/index.ts AND lib/index.js)
{ "name": "my-pkg", "main": "./lib/index.js" }
// after: declare an explicit source entry
{ "name": "my-pkg", "main": "./lib/index.js", "source": "./src/index.ts" }
Defensive patterns

Strategy: validation

Validate before calling

// pre-flight check per workspace package
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
const ambiguous = ['src/index.ts','index.ts','lib/index.js'].filter(f => fs.existsSync(f));
if (!pkg.exports && !pkg.source && ambiguous.length > 1) {
  console.warn(`declare \`source\` or exports; ambiguous entries: ${ambiguous.join(', ')}`);
}

Prevention

When it happens

Trigger: Analyzing a workspace package whose package.json declares neither `exports` nor an unambiguous `source`/single-lib entry, while more than one conventional candidate source file exists on disk (discovered.ambiguous non-empty). Raised from readManifest during `gitnexus analyze` of a Node workspace.

Common situations: Packages with both a compiled `lib/index.js` and a `src/index.ts`; packages publishing multiple top-level bundles; legacy packages migrated from a build layout but never declaring `exports` or `source` in package.json.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-09-15). Data as JSON: /api/errors/fe7782936447135a. Report an issue: GitHub.

Appendix: source

Thrown at gitnexus/src/core/ingestion/import-resolvers/node-workspace-packages.ts:649

  // resolved calls against ~845 raw. Tried in a fixed order, and ONLY appended
  // (declared entries keep precedence): `source` / `publishConfig.source`, the
  // vite `lib.entry`, then `src/main` / `src/index`. More than one candidate
  // that exists on disk is recorded as ambiguous rather than picked — a wrong
  // entry binds every import of the package to the wrong file.
  // A package whose `exports` map has no `"."` (only subpaths) refuses the bare
  // specifier outright; discovery must not manufacture a `src/index` root for it.
  const rootlessExports = declaresExports && rootExports.length === 0;
  const discovered = rootlessExports
    ? { entries: [], ambiguous: [], allowConventional: false }
    : await discoverSourceEntries(parsed, dir, repoRoot);
  for (const entry of discovered.entries) push(entries, entry);
  if (!declaresExports && discovered.allowConventional) {
    for (const conventional of ['src/index', 'index', 'lib/index']) {
      push(entries, joinRepoPath(packageDir, conventional));
    }
  }
  if (discovered.ambiguous.length > 0) {
    logger.warn(
      `[node] package ${name}: ${discovered.ambiguous.length} candidate source entries (${discovered.ambiguous.join(', ')}) — none adopted; declare \`source\` or a single lib entry`,
    );
  }

  const subpathImports = new Map<string, readonly string[]>();
  collectImports(parsed.imports, subpathImports, rebase);

  return { name, package: { dir: packageDir, entries, subpathExports, subpathImports } };
}

/**
 * Walk an `exports` value into the root-entry list and the subpath map.
 *
 * `exports` nests three ways at once — a bare string, a subpath map, and
 * condition maps (`import` / `require` / `types` / `default`) at any depth — so
 * this collects string leaves per subpath rather than assuming a shape.
 */
function collectExports(

View on GitHub (pinned to ac9a4e9abd)