angular/angular · error · FatalDiagnosticError

DUPLICATE_BINDING_NAME

DUPLICATE_BINDING_NAME

Error message

Input '${bindingPropertyName}' is bound to both '${firstMember.name}' and '${member.name}'.

What it means

Thrown when two inputs in the same directive/component resolve to the same binding property name. After each member's InputMapping is computed, its bindingPropertyName (alias if given, else the member name) is tracked in a Map; a repeat raises DUPLICATE_BINDING_NAME with `Input 'x' is bound to both 'first' and 'second'`, plus related information pointing at the first declaration. Ambiguous template bindings cannot be emitted, so compilation stops.

Source

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

    const inputMapping = tryParseInputFieldMapping(
      clazz,
      member,
      evaluator,
      reflector,
      importTracker,
      isCore,
      refEmitter,
      compilationMode,
      emitDeclarationOnly,
    );
    if (inputMapping === null) {
      continue;
    }

    const bindingPropertyName = inputMapping.bindingPropertyName;
    if (bindings.has(bindingPropertyName)) {
      const firstMember = bindings.get(bindingPropertyName)!;
      throw new FatalDiagnosticError(
        ErrorCode.DUPLICATE_BINDING_NAME,
        member.node ?? clazz,
        `Input '${bindingPropertyName}' is bound to both '${firstMember.name}' and '${member.name}'.`,
        [makeRelatedInformation(firstMember.node ?? clazz, `The first binding is declared here.`)],
      );
    }
    bindings.set(bindingPropertyName, member);

    if (member.isStatic) {
      throw new FatalDiagnosticError(
        ErrorCode.INCORRECTLY_DECLARED_ON_STATIC_MEMBER,
        member.node ?? clazz,
        `Input "${member.name}" is incorrectly declared as static member of "${clazz.name.text}".`,
      );
    }

    // Validate that signal inputs are not accidentally declared in the `inputs` metadata.
    if (inputMapping.isSignal && Object.hasOwn(inputsFromClassDecorator, classPropertyName)) {

View on GitHub (pinned to 51cb07e980)

Solutions

  1. Make every binding property name unique — rename or drop one alias: `@Input('firstLabel') firstName` / `@Input('lastLabel') lastName`.
  2. Check the `inputs: ['alias: prop']` metadata array for entries duplicating a decorated member's name or alias.
  3. Use the related-information location in the diagnostic to find the first binding and fix the second.

Example fix

// before
@Input('label') firstName = '';
@Input('label') lastName = '';

// after
@Input('firstLabel') firstName = '';
@Input('lastLabel') lastName = '';
Defensive patterns

Strategy: validation

Validate before calling

// collect input binding names per class and fail on duplicates
function checkDuplicateInputAliases(members: {prop: string; alias?: string}[]) {
  const seen = new Map<string, string>();
  for (const m of members) {
    const binding = m.alias ?? m.prop;
    if (seen.has(binding)) {
      throw new Error(`Input '${binding}' bound to both '${seen.get(binding)}' and '${m.prop}'`);
    }
    seen.set(binding, m.prop);
  }
}

Prevention

When it happens

Trigger: `@Input('label') labelA = ''; @Input('label') labelB = '';` — or an alias colliding with another member's plain name: `@Input() label = ''; @Input('label') other = '';`.

Common situations: Copy-pasted aliased inputs; renames that accidentally create alias/name collisions; entries in the class-decorator `inputs: ['alias: prop']` metadata array overlapping a decorated member's name; merging two components.

Related errors


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