angular/angular-cli · error

Failed to decode "${path}" as UTF-8 text.

Error message

Failed to decode "${path}" as UTF-8 text.

What it means

Thrown by HostTree.readText when the file's bytes are not valid UTF-8. It uses a TextDecoder with fatal:true, so invalid sequences raise a TypeError (or ERR_ENCODING_INVALID_ENCODED_DATA under Jest), which is wrapped into this error with the original kept as `cause`. The library throws because readText promises a string, and silently replacing bytes would hide data corruption.

Source

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

  readText(path: string): string {
    const data = this.read(path);
    if (data === null) {
      throw new FileDoesNotExistException(path);
    }

    const decoder = new TextDecoder('utf-8', { fatal: true });

    try {
      // With the `fatal` option enabled, invalid data will throw a TypeError
      return decoder.decode(data);
    } catch (e) {
      // The second part should not be needed. But Jest does not support instanceof correctly.
      // See: https://github.com/jestjs/jest/issues/2549
      if (
        e instanceof TypeError ||
        (e as NodeJS.ErrnoException).code === 'ERR_ENCODING_INVALID_ENCODED_DATA'
      ) {
        throw new Error(`Failed to decode "${path}" as UTF-8 text.`, { cause: e });
      }
      throw e;
    }
  }

  readJson(path: string): JsonValue {
    const content = this.readText(path);
    const errors: ParseError[] = [];
    const result = jsoncParse(content, errors, { allowTrailingComma: true });

    // If there is a parse error throw with the error information
    if (errors[0]) {
      const { error, offset } = errors[0];
      throw new Error(
        `Failed to parse "${path}" as JSON. ${printParseErrorCode(error)} at offset: ${offset}.`,
      );
    }

View on GitHub (pinned to bb72145f9a)

Solutions

  1. Filter to text-only extensions before calling readText, or use read() (returns Buffer) for unknown files.
  2. Check decodability beforehand with new TextDecoder('utf-8', {fatal:true}).decode(buffer) in a try/catch.
  3. Re-encode the offending file to UTF-8 in the repository, or detect its charset (e.g. with chardet/iconv-lite) and decode explicitly.

Example fix

// before
const text = tree.readText(filePath);
// after
const buf = tree.read(filePath);
if (!buf) throw new Error('missing');
let text: string;
try {
  text = new TextDecoder('utf-8', { fatal: true }).decode(buf);
} catch {
  return; // skip binary file
}
Defensive patterns

Strategy: validation

Validate before calling

const buf = tree.read(path);
const isText = buf !== null && (() => { try { new TextDecoder('utf-8', { fatal: true }).decode(buf); return true; } catch { return false; } })();

Type guard

function isUtf8Text(buf: Buffer): boolean {
  try { new TextDecoder('utf-8', { fatal: true }).decode(buf); return true; } catch { return false; }
}

Try / catch

try {
  const text = tree.readText(path);
} catch (e) {
  if ((e as Error).message.includes('Failed to decode')) {
    // treat as binary: use tree.read(path) Buffer instead
  } else throw e;
}

Prevention

When it happens

Trigger: Calling tree.readText(path) on a binary file (images, fonts, .ico, .zip, compiled assets) or a file encoded in a non-UTF-8 charset (e.g. latin-1/GBK with high-byte characters).

Common situations: Schematics/rules that iterate all files in a directory and readText each one without filtering binary extensions; legacy projects with non-UTF-8 source files; reading assets committed to the tree by a copy operation.

Understand the failure class

Related errors


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