{"record":{"id":"67f5caaad2c76f17","repo":"siyuan-note/siyuan","slug":"template-document-tree-plan-contains-an-invalid-pa-67f5ca","errorCode":null,"errorMessage":"template document tree plan contains an invalid parent operation","messagePattern":"template document tree plan contains an invalid parent operation","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/template_doc_tree.go","lineNumber":550,"sourceCode":"\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] {\n\t\t\treturn errors.New(\"template document tree plan parent operations are not reversible\")\n\t\t}\n\t\tundoOperations[key]--\n\t}\n\tif !hasContentMutation {\n\t\treturn errors.New(\"template document tree plan requires a parent content operation\")\n\t}\n\tfor _, count := range undoOperations {","sourceCodeStart":532,"sourceCodeEnd":568,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/template_doc_tree.go#L532-L568","documentation":"The mirror check for forward operations: every DoOperation in the plan transaction must be non-nil with a non-empty ID. This error means the transaction's forward (do) operations include a nil entry or an operation lacking a block ID, so no inverse operation can ever be matched for it.","triggerScenarios":"Hand-building a plan transaction with a nil or ID-less DoOperation; a client bug that appends placeholder operations; deserialization producing empty Operation.ID for some entries.","commonSituations":"Custom automation scripts calling the transactions API with partially filled operations; test fixtures missing IDs; older client formats that omit IDs for some actions.","solutions":["Set Operation.ID on every DoOperation (the block being inserted/updated/deleted/folded)","Filter out nil entries from DoOperations before submission","Validate all forward operations client-side before calling the transactions endpoint","Regenerate the transaction using the current kernel client library instead of hand-assembling it"],"exampleFix":"// before\ndoOps := []*Operation{{Action: \"insert\", ParentID: parentID}} // no ID\n// after\ndoOps := []*Operation{{Action: \"insert\", ID: newBlockID, ParentID: parentID}}","handlingStrategy":"validation","validationCode":"for i, op := range tx.DoOperations { if op == nil || op.ID == \"\" { return fmt.Errorf(\"do operation %d missing ID\", i) } }","typeGuard":"func validDoOps(ops []*Operation) bool { for _, op := range ops { if op == nil || op.ID == \"\" { return false } }; return len(ops) > 0 }","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"invalid parent operation\") { fix the client builder to assign Operation.ID on every do op, then resubmit }","preventionTips":["Set Operation.ID on every forward operation","Filter nil entries from DoOperations before submission","Validate operations client-side before calling the transactions API","Regenerate transactions with the current client library rather than hand-assembly"],"tags":["go","transaction","validation"],"backgroundTag":"empty-required-field","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}