angular/angular-cli · error · PathIsFileException

Path "${p}" is a file.

Error message

Path "${p}" is a file.

What it means

HostTree.getDir(path) returns the DirEntry for a path, but throws PathIsFileException when the path actually points to a file. Directory traversal APIs (dir(), subDirs, root) route through getDir, so the same throw occurs when one of those receives a file path.

Source

Thrown at packages/angular_devkit/schematics/src/tree/host-tree.ts:357

    return this._recordSync.isFile(this._normalizePath(path));
  }

  get(path: string): FileEntry | null {
    const p = this._normalizePath(path);
    if (this._recordSync.isDirectory(p)) {
      throw new PathIsDirectoryException(p);
    }
    if (!this._recordSync.exists(p)) {
      return null;
    }

    return new LazyFileEntry(p, () => Buffer.from(this._recordSync.read(p)));
  }

  getDir(path: string): DirEntry {
    const p = this._normalizePath(path);
    if (this._recordSync.isFile(p)) {
      throw new PathIsFileException(p);
    }

    let maybeCache = this._dirCache.get(p);
    if (!maybeCache) {
      let parent: Path | null = dirname(p);
      if (p === parent) {
        parent = null;
      }

      maybeCache = new HostDirEntry(parent && this.getDir(parent), p, this._recordSync, this);
      this._dirCache.set(p, maybeCache);
    }

    return maybeCache;
  }
  visit(visitor: FileVisitor): void {
    this.root.visit((path, entry) => {
      visitor(path, entry);

View on GitHub (pinned to bb72145f9a)

Solutions

  1. Verify the path is a directory before calling getDir: if (tree.exists(p)) it's a file — use tree.get(p) instead.
  2. Print/log the resolved path; fix the path construction so it targets a directory.
  3. If files named like directories are legal, branch on tree.exists() before choosing get vs getDir.

Example fix

// before
const dir = tree.getDir(targetPath);
// after
const dir = tree.exists(targetPath) ? undefined : tree.getDir(targetPath);
if (!dir) {
  throw new Error(`${targetPath} is a file, expected a directory`);
}
Defensive patterns

Strategy: validation

Validate before calling

if (tree.exists(dirPath)) {
  throw new Error(`${dirPath} is a file, expected a directory`);
}

Type guard

function isDirectory(tree: Tree, path: string): boolean {
  return !tree.exists(path); // not a file; combined with getDir success means directory
}

Try / catch

let dir;
try {
  dir = tree.getDir(p);
} catch (e) {
  if ((e as any).constructor?.name === 'PathIsFileException') {
    // treat as file: tree.get(p)
  } else throw e;
}

Prevention

When it happens

Trigger: Calling tree.getDir(path), tree.dir(path), or dirEntry.subDir(...) where the path is an existing file; navigating with a segment that accidentally matches a file name (e.g. a file named 'src' or 'app').

Common situations: Building paths from user config where a value was intended to be a folder; projects that have files where directories were expected; off-by-one path joins that drop the final segment.

Related errors


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