angular/angular-cli · error · Error

Could not determine package name for specifier: ${specifier}

Error message

Could not determine package name for specifier: ${specifier}

What it means

Thrown by PackageManager.getManifest after installing a file/remote/git specifier into a temporary directory: the CLI reads the temp package.json expecting the installed package to appear in `dependencies`, but none exists. Without a package name it cannot locate `<temp>/node_modules/<name>/package.json` to read the real manifest.

Source

Thrown at packages/angular/cli/src/package-managers/package-manager.ts:628

        if (!fetchSpec) {
          throw new Error(`Could not parse location from specifier: ${specifier}`);
        }

        // Caching is not supported for non-registry specifiers.
        const { workingDirectory, cleanup } = await this.acquireTempPackage(fetchSpec, {
          ...options,
          ignoreScripts: true,
        });

        try {
          // Discover the package name by reading the temporary `package.json` file.
          // The package manager will have added the package to the `dependencies`.
          const tempManifest = await this.host.readFile(join(workingDirectory, 'package.json'));
          const { dependencies } = JSON.parse(tempManifest) as PackageManifest;
          const packageName = dependencies && Object.keys(dependencies)[0];

          if (!packageName) {
            throw new Error(`Could not determine package name for specifier: ${specifier}`);
          }

          // The package will be installed in `<temp>/node_modules/<name>`.
          const packagePath = join(workingDirectory, 'node_modules', packageName);
          const manifestPath = join(packagePath, 'package.json');
          const manifest = await this.host.readFile(manifestPath);

          return JSON.parse(manifest);
        } finally {
          await cleanup();
        }
      }
      default:
        throw new Error(`Unsupported package specifier type: ${type}`);
    }
  }

  private async getTemporaryDirectory(): Promise<string | undefined> {

View on GitHub (pinned to bb72145f9a)

Solutions

  1. Retry with an explicit tarball or git URL specifier so the package is packed and recorded under dependencies in the temp install.
  2. Ensure the target package itself has a valid name in its package.json and installs normally (run `npm pack`/`npm install <specifier>` manually to confirm).
  3. Clear any package-manager cache/state and retry to rule out a corrupted temp install.
  4. If programmatic, pass a registry specifier ('name@version') instead so the name is known up front.

Example fix

// before
await pm.getManifest('./some/local/dir'); // may not record a dependency
// after
await pm.getManifest('https://registry.npmjs.org/@angular/cli/-/cli-17.0.0.tgz');
Defensive patterns

Strategy: try-catch

Validate before calling

const r = npa(specifier);
if (r.type === 'directory') {
  const pkg = JSON.parse(fs.readFileSync(path.join(r.fetchSpec, 'package.json'), 'utf8'));
  if (!pkg.name) throw new Error(`Package at ${r.fetchSpec} has no name`);
}

Type guard

function isPackageManifest(v: unknown): v is PackageManifest & { dependencies: Record<string, string> } {
  return typeof v === 'object' && v !== null &&
    'dependencies' in v && typeof (v as any).dependencies === 'object' &&
    Object.keys((v as any).dependencies).length > 0;
}

Try / catch

try {
  const manifest = await pm.getManifest(specifier);
} catch (err) {
  if ((err as Error).message.startsWith('Could not determine package name for specifier')) {
    logger.error(`Install of '${specifier}' did not record a dependency; try a tarball URL`);
  } else throw err;
}

Prevention

When it happens

Trigger: getManifest called with a file/remote/git specifier where the temporary install produces a package.json with an empty or missing `dependencies` object — e.g. installing a local directory specifier that resolves as a workspace/link rather than a packed dependency, or a package manager that records the dependency outside `dependencies`.

Common situations: Pointing getManifest at a local folder that gets symlinked instead of installed, using a package manager whose lock/install behavior omits dependencies entries, a corrupted or incomplete temp install due to network or script failures with ignoreScripts, or specifier types whose resolution skips dependency recording.

Related errors


AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30). Data as JSON: /api/errors/c38d7c711fe1e0bf. Report an issue: GitHub.