{"record":{"id":"e4fcf92b8a38d8a5","repo":"siyuan-note/siyuan","slug":"template-document-tree-plan-contains-an-unsupporte","errorCode":null,"errorMessage":"template document tree plan contains an unsupported parent undo operation","messagePattern":"template document tree plan contains an unsupported parent undo operation","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/template_doc_tree.go","lineNumber":542,"sourceCode":"\treturn true, nil\n}\n\nfunc validateTemplateDocTreeParentOperations(transaction *Transaction) error {\n\tinverseActions := map[string]string{\n\t\t\"insert\":        \"delete\",\n\t\t\"delete\":        \"insert\",\n\t\t\"update\":        \"update\",\n\t\t\"foldHeading\":   \"unfoldHeading\",\n\t\t\"unfoldHeading\": \"foldHeading\",\n\t\t\"setAttrs\":      \"setAttrs\",\n\t}\n\tundoOperations := map[string]int{}\n\tfor _, operation := range transaction.UndoOperations {\n\t\tif nil == operation || \"\" == operation.ID {\n\t\t\treturn errors.New(\"template document tree plan contains an invalid parent undo operation\")\n\t\t}\n\t\tif _, supported := inverseActions[operation.Action]; !supported || \"\" != operation.RootID {\n\t\t\treturn errors.New(\"template document tree plan contains an unsupported parent undo operation\")\n\t\t}\n\t\tundoOperations[operation.Action+\"\\x00\"+operation.ID]++\n\t}\n\n\thasContentMutation := false\n\tfor _, operation := range transaction.DoOperations {\n\t\tif nil == operation || \"\" == operation.ID {\n\t\t\treturn errors.New(\"template document tree plan contains an invalid parent operation\")\n\t\t}\n\t\tinverseAction, supported := inverseActions[operation.Action]\n\t\tif !supported || \"\" != operation.RootID {\n\t\t\treturn errors.New(\"template document tree plan contains an unsupported parent operation\")\n\t\t}\n\t\tif \"insert\" == operation.Action || \"delete\" == operation.Action || \"update\" == operation.Action {\n\t\t\thasContentMutation = true\n\t\t}\n\t\tkey := inverseAction + \"\\x00\" + operation.ID\n\t\tif 1 > undoOperations[key] {","sourceCodeStart":524,"sourceCodeEnd":560,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/template_doc_tree.go#L524-L560","documentation":"Each parent UndoOperation's Action must have an entry in the inverseActions table (insert, delete, update, foldHeading, unfoldHeading, setAttrs) and must have an empty RootID. This error means an undo operation uses an unsupported action type or is scoped to a root document, which the template plan machinery cannot reverse as part of the shared undo record.","triggerScenarios":"Including operations with actions like moveBlock/appendBlock/outline in the parent transaction's UndoOperations; setting operation.RootID on parent ops (RootID is reserved for the plan's generated child-document operations); auto-generated undo ops from other features mixed into the plan transaction.","commonSituations":"Integration code that copies an entire transaction's undo list (from unrelated edits) into a plan transaction; newer kernel actions not covered by the plan validator; misuse of RootID to scope parent edits.","solutions":["Restrict parent Do/Undo operations to actions in the inverse set: insert, delete, update, foldHeading, unfoldHeading, setAttrs","Leave RootID empty (\"\") on all parent operations","Split unrelated edits into their own transactions; the plan transaction may contain only the parent-document content mutation and its inverse","Upgrade the kernel if a newly supported action is required — the allowlist is version-dependent"],"exampleFix":"// before\nundoOps := []*Operation{{Action: \"moveBlock\", RootID: rootID, ID: blkID}}\n// after\nundoOps := []*Operation{{Action: \"delete\", ID: blkID}} // supported action, empty RootID","handlingStrategy":"validation","validationCode":"supported := map[string]bool{\"insert\":true,\"delete\":true,\"update\":true,\"foldHeading\":true,\"unfoldHeading\":true,\"setAttrs\":true}; for _, op := range tx.UndoOperations { if !supported[op.Action] || op.RootID != \"\" { reject before submit } }","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"unsupported parent undo operation\") { move the unsupported ops into a separate plain transaction and resubmit the plan transaction with only allowlisted ops }","preventionTips":["Whitelist parent actions to the inverseActions set before building plan transactions","Never set RootID on parent operations","Keep unrelated edits out of plan transactions","Re-check the allowlist after kernel upgrades"],"tags":["go","transaction","validation","undo","unsupported-action"],"backgroundTag":"unsupported-operation","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}