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
- Rewrite the inner rule to mutate the passed tree (host.create/overwrite/delete) and return it unchanged.
- Use mergeWith(source)(tree) which returns the same tree, rather than returning the merged result object.
- 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
- Inside applyToSubtree, always write rules as (tree) => { mutate(tree); return tree; }.
- Never return mergeWith/branch results or new Tree instances in these rules.
- Add a lint/test asserting identity-return for rules used in subtrees.
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
- Tree type is not supported.
- A merge conflicted on path "${path}".
- Path "${record instanceof UpdateRecorderBase ? record.path :
- Cannot create file "${path}".
- Tree type is not supported.
AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30).
Data as JSON: /api/errors/1cd9452ca829f747.
Report an issue: GitHub.