angular/components · error · SchematicsException

Could not read Angular module file: ${modulePath}

Error message

Could not read Angular module file: ${modulePath}

What it means

hasNgModuleImport reads the module file with tree.read(modulePath) before parsing; a null return means the file does not exist in the schematic Tree, so it throws this SchematicsException. This is a pre-parse existence check so the failure names the unreadable file.

Source

Thrown at src/cdk/schematics/utils/ast/ng-module-imports.ts:19

/**
 * @license
 * Copyright Google LLC All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.dev/license
 */

import {SchematicsException, Tree} from '@angular-devkit/schematics';
import * as ts from 'typescript';

/**
 * Whether the Angular module in the given path imports the specified module class name.
 */
export function hasNgModuleImport(tree: Tree, modulePath: string, className: string): boolean {
  const moduleFileContent = tree.read(modulePath);

  if (!moduleFileContent) {
    throw new SchematicsException(`Could not read Angular module file: ${modulePath}`);
  }

  const parsedFile = ts.createSourceFile(
    modulePath,
    moduleFileContent.toString(),
    ts.ScriptTarget.Latest,
    true,
  );
  const ngModuleMetadata = findNgModuleMetadata(parsedFile);

  if (!ngModuleMetadata) {
    throw new SchematicsException(`Could not find NgModule declaration inside: "${modulePath}"`);
  }

  for (let property of ngModuleMetadata!.properties) {
    if (
      !ts.isPropertyAssignment(property) ||
      property.name.getText() !== 'imports' ||

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Check the path exists before calling: tree.exists(modulePath) — and use the correct path when it returns false.
  2. Run the schematic from the workspace root so relative module paths resolve correctly.
  3. If the file was created earlier in the same schematic run, confirm the rule committed the change to the Tree before reading it.
  4. Update the utility's caller to use the project's actual root from workspace.getProject(target).sourceRoot.

Example fix

// before
hasNgModuleImport(tree, 'src/app/app.module.ts', 'BrowserAnimationsModule');
// after
const modulePath = 'src/app/app.module.ts';
if (!tree.exists(modulePath)) {
  throw new Error(`Module file missing: ${modulePath}`);
}
hasNgModuleImport(tree, modulePath, 'BrowserAnimationsModule');
Defensive patterns

Strategy: validation

Validate before calling

import { Tree } from '@angular-devkit/schematics';
function canReadModule(tree: Tree, modulePath: string): boolean {
  return typeof modulePath === 'string' && tree.exists(modulePath);
}
if (!canReadModule(tree, modulePath)) {
  throw new Error(`Fix modulePath before calling hasNgModuleImport: ${modulePath}`);
}

Type guard

function isReadablePath(tree: Tree, path: unknown): path is string {
  return typeof path === 'string' && path.length > 0 && tree.exists(path);
}

Try / catch

try {
  const has = hasNgModuleImport(tree, modulePath, className);
} catch (e) {
  if (e instanceof SchematicsException && e.message.startsWith('Could not read Angular module file:')) {
    console.warn(`Module file unreadable: ${modulePath}`);
    return false;
  }
  throw e;
}

Prevention

When it happens

Trigger: hasNgModuleImport(tree, modulePath, className) called with a path outside the Tree's root, a misspelled module file name, or when the schematic's tree lacks the file because a prior rule wrote it to a different path (the in-memory Tree diverged from disk).

Common situations: Using this utility in setup/side-menu schematics of component libraries where the app module path is guessed from templates (e.g. src/app/app.module.ts) but the workspace layout differs (Nx apps/, libs/), or invoking a schematic from a subdirectory so relative paths resolve against the wrong root.

Related errors


AI-assisted analysis of angular/components@0411926e7d (2026-08-31). Data as JSON: /api/errors/21570e17c1810317. Report an issue: GitHub.