angular/angular-cli · error · PathIsFileException

Path is a file.

Error message

Path is a file.

What it means

During _write, after handling the target itself, the memory host walks every fragment of the path from the root to ensure each intermediate segment is a directory. If any fragment lookup hits an existing FILE entry (`maybeStats.isFile()`), it throws PathIsFileException because a file cannot act as a parent directory. This keeps the in-memory tree structurally valid.

Source

Thrown at packages/angular_devkit/core/src/virtual-fs/host/memory.ts:170

   * List of protected methods that give direct access outside the observables to the cache
   * and internal states.
   */
  protected _write(path: Path, content: FileBuffer): void {
    path = this._toAbsolute(path);
    const old = this._cache.get(path);
    if (old && old.isDirectory()) {
      throw new PathIsDirectoryException(path);
    }

    // Update all directories. If we find a file we know it's an invalid write.
    const fragments = split(path);
    let curr: Path = normalize('/');
    for (const fr of fragments) {
      curr = join(curr, fr);
      const maybeStats = this._cache.get(fr);
      if (maybeStats) {
        if (maybeStats.isFile()) {
          throw new PathIsFileException(curr);
        }
      } else {
        this._cache.set(curr, this._newDirStats());
      }
    }

    // Create the stats.
    const stats: Stats<SimpleMemoryHostStats> = this._newFileStats(content, old);
    this._cache.set(path, stats);
    this._updateWatchers(path, old ? HostWatchEventType.Changed : HostWatchEventType.Created);
  }
  protected _read(path: Path): FileBuffer {
    path = this._toAbsolute(path);
    const maybeStats = this._cache.get(path);
    if (!maybeStats) {
      throw new FileDoesNotExistException(path);
    } else if (maybeStats.isDirectory()) {
      throw new PathIsDirectoryException(path);

View on GitHub (pinned to bb72145f9a)

Solutions

  1. Fix the constructed path so no intermediate segment is an existing file; inspect each fragment with host.exists()/host.isFile().
  2. Rename or delete the conflicting file entry if it is no longer needed, then retry the write.
  3. Use a different output location to avoid a file name doubling as a directory name.
  4. Normalize/validate user-supplied or config-supplied paths before passing them to write().

Example fix

// before
host.write(join(basePath, 'README.md', 'intro.txt'), content); // README.md is a file
// after
const target = join(basePath, 'docs', 'intro.txt');
host.write(target, content);
Defensive patterns

Strategy: validation

Validate before calling

import { join, normalize, Path, split } from '@angular-devkit/core';

function assertValidFilePath(host: { exists(p: Path): boolean; isFile(p: Path): boolean }, filePath: Path): void {
  let curr: Path = normalize('/');
  for (const fr of split(normalize(filePath))) {
    curr = join(curr, fr);
    if (host.exists(curr) && host.isFile(curr) && curr !== normalize(filePath)) {
      throw new Error(`'${curr}' is a file but is used as a directory in path '${filePath}'`);
    }
  }
}

Try / catch

import { PathIsFileException } from '@angular-devkit/core';

try {
  host.write(path, buffer);
} catch (e) {
  if (e instanceof PathIsFileException) {
    throw new Error(`Fix path construction: '${e.path}' collides with an existing file`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling host.write('some/existingfile.txt/child.txt', content) — the fragment 'existingfile.txt' exists in the cache as a file, so creating 'child.txt' under it throws. Also triggered when a prior write created a file whose name now collides with a directory component of a deeper path.

Common situations: Joining paths from user input or config where a filename accidentally becomes a folder (e.g. baseDir already contains a filename); path concat bugs with template strings ('path' + file) missing a separator; case where a schematic generated 'foo' as a file earlier and later writes 'foo/bar'.

Related errors


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