angular/angular-cli · error · Error

Could not parse directory path from specifier: ${specifier}

Error message

Could not parse directory path from specifier: ${specifier}

What it means

In getManifest(), when npa resolves the specifier to type 'directory', the resolved `fetchSpec` is the directory path; the manifest is read from `<dir>/package.json`. If `fetchSpec` is falsy — meaning npa classified it as a directory but could not extract the actual path — this Error is thrown. It signals a malformed directory-style specifier.

Source

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

            }
            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) ?? '';
          }
          if (!versionSpec) {
            return null;
          }
        }

        return this.getRegistryManifest(name, versionSpec, options);
      }
      case 'directory': {
        if (!fetchSpec) {
          throw new Error(`Could not parse directory path from specifier: ${specifier}`);
        }

        const manifestPath = join(fetchSpec, 'package.json');
        const manifest = await this.host.readFile(manifestPath);

        return JSON.parse(manifest);
      }
      case 'file':
      case 'remote':
      case 'git': {
        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,

View on GitHub (pinned to bb72145f9a)

Solutions

  1. Pass a complete directory specifier with a non-empty path, e.g. 'file:./packages/my-lib' instead of 'file:' or 'file:./'.
  2. Resolve the path to an absolute directory with path.resolve() before constructing the specifier.
  3. Verify the referenced directory exists and contains a package.json before calling manifest().
  4. If building specifiers from config variables, validate the path segment is non-empty and a real directory first.

Example fix

// before
await pm.getManifest('file:');

// after
import { resolve, existsSync } from 'node:fs';
const dir = resolve('packages/my-lib');
if (!existsSync(resolve(dir, 'package.json'))) {
  throw new Error(`No package.json in directory: ${dir}`);
}
await pm.getManifest(`file:${dir}`);
Defensive patterns

Strategy: validation

Validate before calling

import npa from 'npm-package-arg';
import { existsSync, statSync } from 'node:fs';
import { resolve } from 'node:path';
function assertDirectorySpec(specifier: string): string {
  const parsed = npa(specifier);
  if (parsed.type === 'directory') {
    if (!parsed.fetchSpec) {
      throw new Error(`Directory specifier missing a path: "${specifier}"`);
    }
    const dir = resolve(parsed.fetchSpec);
    if (!existsSync(dir) || !statSync(dir).isDirectory() || !existsSync(resolve(dir, 'package.json'))) {
      throw new Error(`Directory has no package.json: ${dir}`);
    }
    return dir;
  }
  return specifier;
}
// call before pm.getManifest(specifier): assertDirectorySpec(specifier);

Type guard

function hasDirectoryPath(parsed: npa.Result): parsed is npa.Result & { fetchSpec: string } {
  return parsed.type === 'directory' && typeof parsed.fetchSpec === 'string' && parsed.fetchSpec.length > 0;
}

Try / catch

try {
  const manifest = await pm.getManifest(specifier);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Could not parse directory path from specifier')) {
    console.error(`Provide a non-empty directory path, e.g. file:./packages/lib: ${e.message}`);
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling manifest(specifier) with a directory specifier such as 'file:./', 'file:', or a npa.Result of type 'directory' whose fetchSpec resolved to an empty string, so no directory path is available to read package.json from.

Common situations: Passing 'file:' with an empty path from templated config; referencing a directory specifier whose path portion was stripped by over-trimming; programmatic specifier assembly where the path variable was empty or undefined; workspace globs resolved to empty strings.

Related errors


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