angular/angular · error · Error

Invalid function call: It should have only a single object l

Error message

Invalid function call: It should have only a single object literal argument, but contained ${args.length}.

What it means

FileLinker.linkPartialDeclaration requires ɵɵngDeclare* calls to receive exactly one argument: the object literal holding R3DeclareDirectiveMetadata / R3DeclareComponentMetadata (and friends). Zero or multiple arguments means the call is not compiler-generated output, and the linker throws with the actual argument count.

Source

Thrown at packages/compiler-cli/linker/src/file_linker/file_linker.ts:65

  /**
   * Link the metadata extracted from the args of a call to a partial declaration function.
   *
   * The `declarationScope` is used to determine the scope and strategy of emission of the linked
   * definition and any shared constant statements.
   *
   * @param declarationFn the name of the function used to declare the partial declaration - e.g.
   *     `ɵɵngDeclareDirective`.
   * @param args the arguments passed to the declaration function, should be a single object that
   *     corresponds to the `R3DeclareDirectiveMetadata` or `R3DeclareComponentMetadata` interfaces.
   * @param declarationScope the scope that contains this call to the declaration function.
   */
  linkPartialDeclaration(
    declarationFn: string,
    args: TExpression[],
    declarationScope: DeclarationScope<TConstantScope, TExpression>,
  ): TExpression {
    if (args.length !== 1) {
      throw new Error(
        `Invalid function call: It should have only a single object literal argument, but contained ${args.length}.`,
      );
    }

    const metaObj = AstObject.parse<R3PartialDeclaration, TExpression>(
      args[0],
      this.linkerEnvironment.host,
    );
    const ngImport = metaObj.getNode('ngImport');
    const emitScope = this.getEmitScope(ngImport, declarationScope);

    const minVersion = metaObj.getString('minVersion');
    const version = metaObj.getString('version');
    const linker = this.linkerSelector.getLinker(declarationFn, minVersion, version);
    const definition = linker.linkPartialDeclaration(emitScope.constantPool, metaObj, version);

    return emitScope.translateDefinition(definition);
  }

View on GitHub (pinned to 51cb07e980)

Solutions

  1. Never define, wrap, or rename functions with the reserved ɵɵngDeclare* names
  2. Recompile the library with the official Angular compiler
  3. Remove post-processing steps that rewrite these call expressions

Example fix

// before
const declare = ɵɵngDeclareDirective;
declare(MyDir, { selector: '[x]' });
// after
ɵɵngDeclareDirective({ minVersion: '14.1', ngImport: core, type: MyDir, selector: '[x]' });
Defensive patterns

Strategy: type-guard

Validate before calling

import * as t from '@babel/types';
const isPartialDeclarationCall = (node: t.Node): node is t.CallExpression =>
  t.isCallExpression(node) &&
  t.isIdentifier(node.callee) && /^ɵɵngDeclare/.test(node.callee.name) &&
  node.arguments.length === 1 && t.isObjectExpression(node.arguments[0]);

Type guard

const isWellFormedDeclarationCall = (node: t.CallExpression): boolean =>
  t.isIdentifier(node.callee) && /^ɵɵngDeclare/.test(node.callee.name) &&
  node.arguments.length === 1 && t.isObjectExpression(node.arguments[0]);

Prevention

When it happens

Trigger: Hand-written `ɵɵngDeclareDirective()` with no args or extra args; a user-defined wrapper that coincidentally uses the reserved ɵɵngDeclare* name and passes extra parameters; broken codegen splitting the metadata object.

Common situations: Application code defining functions named ɵɵngDeclareDirective/Component/Injectable; post-processing that rewrites declaration calls; monkey-patched library bundles.

Related errors


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