angular/angular-cli · error · Error

Could not parse package name from specifier: ${specifier}

Error message

Could not parse package name from specifier: ${specifier}

What it means

In getManifest(), the specifier is parsed with npm-package-arg (npa). For 'range', 'version', and 'tag' specifier types the resolved `name` must be present to query the registry; if npa could not extract a package name from the given specifier, this Error is thrown. It indicates the specifier string is malformed in a way that yielded a registry-style type but no name.

Source

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

   * as those specified by file paths, directory paths, and remote tarballs.
   * Caching is only supported for registry packages.
   *
   * @param specifier The package specifier to resolve the manifest for.
   * @param options Options for the fetch.
   * @returns A promise that resolves to the `PackageManifest` object, or `null` if the package is not found.
   */
  async getManifest(
    specifier: string | npa.Result,
    options: { timeout?: number; registry?: string; bypassCache?: boolean } = {},
  ): Promise<PackageManifest | null> {
    const { name, type, fetchSpec } = typeof specifier === 'string' ? npa(specifier) : specifier;

    switch (type) {
      case 'range':
      case 'version':
      case 'tag': {
        if (!name) {
          throw new Error(`Could not parse package name from specifier: ${specifier}`);
        }

        // `fetchSpec` is the version, range, or tag.
        let versionSpec = fetchSpec ?? 'latest';
        if (this.descriptor.requiresManifestVersionLookup) {
          if (type === 'tag' || !fetchSpec) {
            const metadata = await this.getRegistryMetadata(name, options);
            if (!metadata) {
              return null;
            }
            versionSpec = metadata['dist-tags'][versionSpec];
          } else if (type === 'range') {
            const metadata = await this.getRegistryMetadata(name, options);
            if (!metadata) {
              return null;
            }
            versionSpec = maxSatisfying(metadata.versions, fetchSpec) ?? '';
          }

View on GitHub (pinned to bb72145f9a)

Solutions

  1. Inspect the specifier string and supply a complete one including the package name, e.g. '@angular/core@^17' instead of '@scope/' or ''.
  2. Validate the specifier with npa before calling: check that the parsed result has a non-empty `name` for range/version/tag types.
  3. If building specifiers from variables, add a check that the name segment is non-empty before interpolation.
  4. If the target is a local path or tarball, use the correct specifier form ('file:./path', 'directory', URL) so it parses to the right type.

Example fix

// before
await pm.getManifest('@myorg/');

// after
const spec = '@myorg/my-package@^1.0.0';
const parsed = npa(spec);
if (!parsed.name) {
  throw new Error(`Specifier must include a package name: ${spec}`);
}
await pm.getManifest(spec);
Defensive patterns

Strategy: validation

Validate before calling

import npa from 'npm-package-arg';
function assertNamedRegistrySpec(specifier: string): npa.Result {
  const parsed = npa(specifier);
  if ((parsed.type === 'range' || parsed.type === 'version' || parsed.type === 'tag') && !parsed.name) {
    throw new Error(`Specifier must include a package name: "${specifier}"`);
  }
  return parsed;
}
// call before pm.getManifest(specifier): assertNamedRegistrySpec(specifier);

Type guard

function hasName(parsed: npa.Result): parsed is npa.Result & { name: string } {
  return typeof parsed.name === 'string' && parsed.name.length > 0;
}

Try / catch

try {
  const manifest = await pm.getManifest(specifier);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Could not parse package name from specifier')) {
    console.error(`Fix specifier, include a package name: ${e.message}`);
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling manifest(specifier) with a string that npa resolves to type range/version/tag but with a falsy `name` — e.g. an empty or whitespace string coerced into a range type, a bare '@scope/' prefix with no package name, or passing a hand-built npa.Result object with name undefined.

Common situations: Typing a specifier like '@scope/' or '' into a tool that forwards it to the CLI; programmatically constructing package specifiers from config values that are missing the package name (e.g. `${scope}/${''}@${version}`); trimming bugs that leave '@' or version delimiters only.

Related errors


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