{"record":{"id":"1cd9452ca829f747","repo":"angular/angular-cli","slug":"original-tree-must-be-returned-from-all-rules-when","errorCode":null,"errorMessage":"Original tree must be returned from all rules when using \"applyToSubtree\".","messagePattern":"Original tree must be returned from all rules when using \"applyToSubtree\"\\.","errorType":"exception","errorClass":"SchematicsException","httpStatus":null,"severity":"error","filePath":"packages/angular_devkit/schematics/src/rules/base.ts","lineNumber":177,"sourceCode":"        // Deleted, just return.\n        return null;\n      }\n    }\n\n    return current;\n  };\n}\n\nexport function applyToSubtree(path: string, rules: Rule[]): Rule {\n  return (tree, context) => {\n    const scoped = new ScopedTree(tree, path);\n\n    return callRule(chain(rules), scoped, context).pipe(\n      map((result) => {\n        if (result === scoped) {\n          return tree;\n        } else {\n          throw new SchematicsException(\n            'Original tree must be returned from all rules when using \"applyToSubtree\".',\n          );\n        }\n      }),\n    );\n  };\n}\n","sourceCodeStart":159,"sourceCodeEnd":185,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular_devkit/schematics/src/rules/base.ts#L159-L185","documentation":"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.","triggerScenarios":"Chaining a rule inside applyToSubtree that returns a new Tree (e.g. mergeWith result, branch(), or an async rule returning another instance).","commonSituations":"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().","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."],"exampleFix":"// before\nreturn (tree: Tree) => mergeWith(source)(tree).pipe(map(t => t.branch()));\n// after\nreturn (tree: Tree) => mergeWith(source)(tree); // returns the original tree instance","handlingStrategy":"validation","validationCode":"// audit inner rules: each must return its input tree\nfunction assertIdentity(rule: Rule): Rule {\n  return (tree: Tree) => {\n    const r = rule(tree);\n    if (r !== tree) throw new Error('rule must return the same tree for applyToSubtree');\n    return r;\n  };\n}","typeGuard":"function returnsSameTree(rule: Rule): boolean {\n  const probe = {} as Tree;\n  return rule(probe) === probe;\n}","tryCatchPattern":"try {\n  return applyToSubtree(path, rules);\n} catch (err) {\n  if (err.message.includes('applyToSubtree')) {\n    console.error('One of the chained rules did not return its input tree');\n  }\n}","preventionTips":["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."],"tags":["schematics","tree","rule-contract"],"backgroundTag":"rule-must-return-original-tree","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}