siyuan-note/siyuan · error
template document tree plan requires reversible parent opera
Error message
template document tree plan requires reversible parent operations
What it means
A document-tree plan transaction must carry both DoOperations and UndoOperations so the generated documents can be rolled back; an empty list on either side triggers this error. The plan mechanism compensates by undoing the parent operation if any part of the tree creation fails, so reversibility is mandatory.
Source
Thrown at kernel/model/template_doc_tree.go:467
func AttachTemplateDocTreePlans(transactions []*Transaction) (attached bool, err error) {
var target *Transaction
for _, transaction := range transactions {
if nil == transaction || "" == transaction.TemplateDocTreePlanID {
continue
}
if nil != target || 1 != len(transactions) {
return false, errors.New("a document tree plan must be applied in a single transaction")
}
target = transaction
}
if nil == target {
return false, nil
}
if target.isReplay {
return false, errors.New("template document tree plans cannot be attached to replay transactions")
}
if 0 == len(target.DoOperations) || 0 == len(target.UndoOperations) {
return false, errors.New("template document tree plan requires reversible parent operations")
}
if err = validateTemplateDocTreeParentOperations(target); nil != err {
return false, err
}
for _, operation := range append(append([]*Operation{}, target.DoOperations...), target.UndoOperations...) {
if nil != operation && ("restoreCreatedDoc" == operation.Action || "removeCreatedDoc" == operation.Action) {
return false, errors.New("template document tree transaction contains a reserved operation")
}
}
planID := target.TemplateDocTreePlanID
target.TemplateDocTreePlanID = ""
value, loaded := templateDocTreePlans.LoadAndDelete(planID)
if !loaded {
return false, errors.New("template document tree plan is missing or has expired")
}
plan, ok := value.(*templateDocTreePlan)
if !ok || plan.id != planID || time.Now().After(plan.expiresAt) {View on GitHub (pinned to 8641553a1f)
Solutions
- Populate UndoOperations with the inverse of every DoOperation when constructing the plan transaction
- Build the transaction through the official template rendering flow (renderTemplateSource) instead of hand-assembling operations
- Do not strip or clear UndoOperations before submission
- Verify both operation arrays are non-empty before calling the transactions API
Example fix
// before: missing undo side
tx := &Transaction{TemplateDocTreePlanID: planID, DoOperations: doOps}
// after
tx := &Transaction{TemplateDocTreePlanID: planID, DoOperations: doOps, UndoOperations: undoOps} Defensive patterns
Strategy: validation
Validate before calling
// Go: verify both operation sides before submitting a plan transaction
reversible := len(tx.DoOperations) > 0 && len(tx.UndoOperations) > 0
if !reversible {
return errors.New("plan transaction needs non-empty Do and Undo operations")
} Try / catch
if _, err := AttachTemplateDocTreePlans([]*Transaction{tx}); err != nil && strings.Contains(err.Error(), "reversible") {
return rebuildPlanTransactionWithUndo(tx)
} Prevention
- Generate plan transactions through renderTemplateSource, never by hand
- Always pair every do operation with its inverse undo operation
- Assert both operation arrays are non-empty before calling the API
- Do not strip UndoOperations when serializing or copying transactions
When it happens
Trigger: Submitting a transaction with TemplateDocTreePlanID set but with no undo operations (or no do operations) in performTransactions — typically a hand-constructed transaction missing the compensating operation list.
Common situations: Plugin or script authors building the plan transaction manually and forgetting UndoOperations; serialization dropping empty operation arrays; a code path that clears undo ops after partial failure and re-submits the transaction.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- a document tree plan must be applied in a single transaction
- template document tree plans cannot be attached to replay tr
- template document tree transaction contains a reserved opera
- template document tree plan contains an invalid parent undo
- template document tree plan contains an unsupported parent u
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/43a964ddc5837c2f.
Report an issue: GitHub.