angular/angular · error · FatalDiagnosticError

LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION

LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION

Error message

In ${compilationModeName} mode, host directive cannot be an expression. Use an identifier instead

What it means

In local compilation mode (tsconfig `angularCompilerOptions.compilationMode: "local"`, or experimental declaration-only emission) each file is compiled without full-program context, so hostDirectives entries must be plain identifiers or property-access chains resolvable locally. An entry that is an arbitrary expression — typically `forwardRef(() => Dir)` — cannot be processed and is rejected with LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION at that expression node.

Source

Thrown at packages/compiler-cli/src/ngtsc/annotations/directive/src/shared.ts:2154

      }
    }

    let directive: Reference<ClassDeclaration> | Expression | ExternalReference;
    let nameForErrors = (fieldName: string) => '@Directive.hostDirectives';
    if (compilationMode === CompilationMode.LOCAL && hostReference instanceof DynamicValue) {
      // At the moment in local compilation we only support simple array for host directives, i.e.,
      // an array consisting of the directive identifiers. We don't support forward refs or other
      // expressions applied on externally imported directives. The main reason is simplicity, and
      // that almost nobody wants to use host directives this way (e.g., what would be the point of
      // forward ref for imported symbols?!)
      if (
        !ts.isIdentifier(hostReference.node) &&
        !ts.isPropertyAccessExpression(hostReference.node)
      ) {
        const compilationModeName = emitDeclarationOnly
          ? 'experimental declaration-only emission'
          : 'local compilation';
        throw new FatalDiagnosticError(
          ErrorCode.LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION,
          hostReference.node,
          `In ${compilationModeName} mode, host directive cannot be an expression. Use an identifier instead`,
        );
      }
      directive = new WrappedNodeExpr(hostReference.node);
    } else if (hostReference instanceof Reference) {
      directive = hostReference as Reference<ClassDeclaration>;
      nameForErrors = (fieldName: string) =>
        `@Directive.hostDirectives.${
          (directive as Reference<ClassDeclaration>).node.name.text
        }.${fieldName}`;
    } else {
      throw new Error('Impossible state');
    }

    const meta: HostDirectiveMeta = {
      directive,

View on GitHub (pinned to 51cb07e980)

Solutions

  1. Import the directive class and reference it directly: `hostDirectives: [MenuHost]`
  2. Break the circular import that motivated forwardRef (restructure files, or use `import type` for type-only usage)
  3. Fall back to full compilation mode if the expression form is genuinely required

Example fix

// before
@Directive({selector: 'menu', hostDirectives: [forwardRef(() => MenuHost)]})
class Menu {}

// after
import {MenuHost} from './menu-host';

@Directive({selector: 'menu', hostDirectives: [MenuHost]})
class Menu {}
Defensive patterns

Strategy: validation

Validate before calling

const HOSTDIR_EXPR = /hostDirectives\s*:\s*\[[^\]]*(forwardRef|\bfunction\b|\(\s*\(\s*\))/;

// CI gate: fail when compilationMode is local (or emitDeclarationOnly) and the
// hostDirectives array contains anything but identifiers/property accesses:
//   rg -n "hostDirectives.*forwardRef" src/
//   rg -n '"compilationMode"\s*:\s*"local"' tsconfig*

Prevention

When it happens

Trigger: `@Directive({hostDirectives: [forwardRef(() => MenuHost)]})` compiled with compilationMode 'local' or emitDeclarationOnly; also IIFE/call expressions or conditional expressions inside the hostDirectives array.

Common situations: Libraries opting into local/partial compilation for faster builds; forwardRef copied from View-Engine-era code to break circular imports; declaration-only builds (ngtsc emitDeclarationOnly) hitting the same restriction.

Related errors


AI-assisted analysis of angular/angular@51cb07e980 (2026-08-22). Data as JSON: /api/errors/3e64ca5115d7ff23. Report an issue: GitHub.