angular/angular-cli · error · SchematicsException

Original tree must be returned from all rules when using "ap

Error message

Original tree must be returned from all rules when using "applyToSubtree".

What it means

applyToSubtree scopes a subtree and requires every chained rule to return the same (scoped) tree instance it received; if any rule returns a different tree, the engine cannot safely merge it back and throws. Rules inside applyToSubtree must be pure in-place mutators returning the input tree.

Source

Thrown at packages/angular_devkit/schematics/src/rules/base.ts:177

        // Deleted, just return.
        return null;
      }
    }

    return current;
  };
}

export function applyToSubtree(path: string, rules: Rule[]): Rule {
  return (tree, context) => {
    const scoped = new ScopedTree(tree, path);

    return callRule(chain(rules), scoped, context).pipe(
      map((result) => {
        if (result === scoped) {
          return tree;
        } else {
          throw new SchematicsException(
            'Original tree must be returned from all rules when using "applyToSubtree".',
          );
        }
      }),
    );
  };
}

View on GitHub (pinned to bb72145f9a)

Solutions

  1. Rewrite the inner rule to mutate the passed tree (host.create/overwrite/delete) and return it unchanged.
  2. Use mergeWith(source)(tree) which returns the same tree, rather than returning the merged result object.
  3. Replace applyToSubtree with manual scoping: move files with the subtree prefix yourself or use scoped hosts.

Example fix

// before
return (tree: Tree) => mergeWith(source)(tree).pipe(map(t => t.branch()));
// after
return (tree: Tree) => mergeWith(source)(tree); // returns the original tree instance
Defensive patterns

Strategy: validation

Validate before calling

// audit inner rules: each must return its input tree
function assertIdentity(rule: Rule): Rule {
  return (tree: Tree) => {
    const r = rule(tree);
    if (r !== tree) throw new Error('rule must return the same tree for applyToSubtree');
    return r;
  };
}

Type guard

function returnsSameTree(rule: Rule): boolean {
  const probe = {} as Tree;
  return rule(probe) === probe;
}

Try / catch

try {
  return applyToSubtree(path, rules);
} catch (err) {
  if (err.message.includes('applyToSubtree')) {
    console.error('One of the chained rules did not return its input tree');
  }
}

Prevention

When it happens

Trigger: Chaining a rule inside applyToSubtree that returns a new Tree (e.g. mergeWith result, branch(), or an async rule returning another instance).

Common situations: Wrapping rules like filter() output incorrectly; a rule that calls mergeWith and returns the merged Observable<Tree> result object instead of mutating and returning the input; custom rules built with Tree.branch().

Related errors


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