angular/angular · error · FatalLinkerError

Errors found in the template: ${errors}

Error message

Errors found in the template:
${errors}

What it means

When linking a partially compiled component, the linker re-parses the stored template with the current compiler using the feature flags negotiated for that declaration (enableBlockSyntax, enableLetSyntax, etc.). Any parse diagnostics are collected, stringified, joined with newlines, and rethrown as a FatalLinkerError listing every error found in the template.

Source

Thrown at packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_component_linker_1.ts:125

    const hasOnPushByDefault = major >= 22 || version === PLACEHOLDER_VERSION;

    const template = parseTemplate(templateInfo.code, templateInfo.sourceUrl, {
      escapedString: templateInfo.isEscaped,
      range: templateInfo.range,
      enableI18nLegacyMessageIdFormat: false,
      preserveWhitespaces: metaObj.has('preserveWhitespaces')
        ? metaObj.getBoolean('preserveWhitespaces')
        : false,
      // We normalize line endings if the template is was inline.
      i18nNormalizeLineEndingsInICUs: isInline,
      enableBlockSyntax,
      enableLetSyntax,
      // TODO(crisbeto): figure out how this is enabled.
      enableSelectorless: false,
    });
    if (template.errors !== null) {
      const errors = template.errors.map((err) => err.toString()).join('\n');
      throw new FatalLinkerError(
        templateSource.expression,
        `Errors found in the template:\n${errors}`,
      );
    }

    let declarationListEmitMode = DeclarationListEmitMode.Direct;

    const extractDeclarationTypeExpr = (
      type: AstValue<o.Expression | (() => o.Expression), TExpression>,
    ) => {
      const {expression, forwardRef} = extractForwardRef(type);
      if (forwardRef === ForwardRefHandling.Unwrapped) {
        declarationListEmitMode = DeclarationListEmitMode.Closure;
      }
      return expression;
    };

    let declarations: R3TemplateDependencyMetadata[] = [];

View on GitHub (pinned to 51cb07e980)

Solutions

  1. Upgrade the app's @angular/* and CLI packages to at least the library's Angular version
  2. Read the joined per-error list — it names the exact syntax and position that failed to parse
  3. Re-link the pristine published bundle to rule out local post-processing corruption
  4. For published libraries, avoid the newest template syntax until consumers can upgrade

Example fix

// before (package.json)
"@angular/core": "^16.2.0", "@angular/compiler-cli": "^16.2.0"
// after
"@angular/core": "^17.3.0", "@angular/compiler-cli": "^17.3.0"
Defensive patterns

Strategy: validation

Validate before calling

import { parseTemplate } from '@angular/compiler';
const result = parseTemplate(templateHtml, sourceUrl);
if (result.errors !== null && result.errors.length > 0) {
  throw new Error(result.errors.map((e) => e.toString()).join('\n'));
}

Try / catch

try {
  runLinker(bundlePath);
} catch (e) {
  if (e instanceof FatalLinkerError && /Errors found in the template/.test(e.message)) {
    // e.message already lists every template error with positions — surface it verbatim
    console.error(e.message);
  }
  throw e;
}

Prevention

When it happens

Trigger: The template uses syntax newer than the linking compiler supports — e.g. `@if` control-flow blocks linked by a pre-17 compiler-cli, or `@let` linked by a pre-18.1 toolchain; template strings corrupted by post-processing; malformed HTML caught on re-parse.

Common situations: An app on an older Angular consuming a library built with a newer Angular (classic version skew); locally rewritten bundles; library templates using very recent template features while consumers pin old versions.

Related errors


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