angular/angular-cli · error · Error

tried to insert ${toInsert} as first occurence with no fallb

Error message

tried to insert ${toInsert} as first occurence with no fallback position

What it means

insertAfterLastOccurrence (a shared AST mutation helper in ast-utils) computes the insertion position from the last occurrence of the given syntax kind among the found nodes. If no nodes were found and no explicit fallbackPos was provided, it cannot determine where to insert and throws this Error.

Source

Thrown at packages/schematics/angular/utility/ast-utils.ts:253

 */
export function insertAfterLastOccurrence(
  nodes: ts.Node[] | ts.NodeArray<ts.Node>,
  toInsert: string,
  file: string,
  fallbackPos: number,
  syntaxKind?: ts.SyntaxKind,
): Change {
  let lastItem: ts.Node | undefined;
  for (const node of nodes) {
    if (!lastItem || lastItem.getStart() < node.getStart()) {
      lastItem = node;
    }
  }
  if (syntaxKind && lastItem) {
    lastItem = findNodes(lastItem, syntaxKind).sort(nodesByPosition).pop();
  }
  if (!lastItem && fallbackPos == undefined) {
    throw new Error(`tried to insert ${toInsert} as first occurence with no fallback position`);
  }
  const lastItemPosition: number = lastItem ? lastItem.getEnd() : fallbackPos;

  return new InsertChange(file, lastItemPosition, toInsert);
}

function _angularImportsFromNode(node: ts.ImportDeclaration): { [name: string]: string } {
  const ms = node.moduleSpecifier;
  let modulePath: string;
  switch (ms.kind) {
    case ts.SyntaxKind.StringLiteral:
      modulePath = (ms as ts.StringLiteral).text;
      break;
    default:
      return {};
  }

  if (!modulePath.startsWith('@angular/')) {

View on GitHub (pinned to bb72145f9a)

Solutions

  1. Pass an explicit `fallbackPos` (e.g. 0 or end-of-file offset) when calling insertAfterLastOccurrence on files that may lack the expected node.
  2. Verify the target file contains the expected syntax (e.g. existing imports) before mutating; create/normalize the file first.
  3. Use ts.createSourceFile to inspect the file and adjust the syntaxKind argument to one actually present.
  4. Regenerate or fix the malformed source file that the codemod is targeting.

Example fix

// before
const change = insertAfterLastOccurrence(nodes, toInsert, file, undefined, ts.SyntaxKind.CommaToken);
// after
const change = insertAfterLastOccurrence(nodes, toInsert, file, 0, ts.SyntaxKind.CommaToken);
Defensive patterns

Strategy: try-catch

Validate before calling

const imports = source.statements.filter(ts.isImportDeclaration);
if (imports.length === 0) {
  console.warn('No import declarations found; pass an explicit fallbackPos to insertAfterLastOccurrence.');
}

Type guard

function hasNodes(nodes: ts.Node[]): nodes is [ts.Node, ...ts.Node[]] {
  return nodes.length > 0;
}

Try / catch

try {
  const change = insertAfterLastOccurrence(nodes, toInsert, file, fallbackPos ?? 0, syntaxKind);
} catch (e) {
  if (String(e?.message).includes('no fallback position')) {
    // file lacks the expected node; insert at a known position instead
  }
}

Prevention

When it happens

Trigger: Calling helpers like insertImport or addRouteDeclarationToModule against a source file whose AST contains no node of the expected syntax kind (e.g. importing into an empty/nonexistent imports section) without passing a fallbackPos, or passing an incorrect syntaxKind for the file's content shape.

Common situations: Custom schematics built on ast-utils running against files that don't match the expected structure (empty files, files with only comments, non-TS content); upgrading Angular where file templates changed; passing wrong syntax kind when writing custom codemods.

Related errors


AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30). Data as JSON: /api/errors/fa4bff5ca9d99f99. Report an issue: GitHub.