siyuan-note/siyuan · error
a document tree plan must be applied in a single transaction
Error message
a document tree plan must be applied in a single transaction
What it means
AttachTemplateDocTreePlans scans a batch of transactions for one carrying a TemplateDocTreePlanID. A plan is only valid when it is the sole transaction in the batch; if more than one transaction is submitted, or a plan transaction coexists with normal ones, the error is thrown. Document-tree plans must commit atomically so their generated documents can be cleaned up or undone as a unit.
Source
Thrown at kernel/model/template_doc_tree.go:456
"title": node.Title,
"id": node.ID,
"parentID": node.ParentID,
"rootID": node.RootID,
"hPath": node.HPath,
"name": "",
"alias": "",
}
}
// AttachTemplateDocTreePlans 将一次性计划转换为内核事务操作,父文档内容与全部子文档共用一条撤销记录。
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")View on GitHub (pinned to 8641553a1f)
Solutions
- Submit the document-tree plan transaction alone in its own /api/transactions request
- Split unrelated block edits into a separate, prior or subsequent transaction call
- Ensure only one transaction in any single batch carries a TemplateDocTreePlanID
- Review retry/queue code so a failed plan transaction is not re-batched with others
Example fix
// before: plan batched with ordinary edits
performTransactions([]*Transaction{planTx, normalTx})
// after: plan committed alone
performTransactions([]*Transaction{planTx})
performTransactions([]*Transaction{normalTx}) Defensive patterns
Strategy: validation
Validate before calling
// Go: ensure a plan transaction is submitted alone
single := len(txs) == 1 && txs[0].TemplateDocTreePlanID != ""
if !single {
return errors.New("submit the document-tree plan transaction by itself")
} Try / catch
ok, err := AttachTemplateDocTreePlans(transactions)
if err != nil && strings.Contains(err.Error(), "single transaction") {
return splitAndRetryIndividually(transactions)
} Prevention
- Always send a template document-tree plan in its own transactions request
- Never batch plan transactions with ordinary block-edit transactions
- Audit client queue/retry code for transaction batching
- Include at most one plan-bearing transaction per API call
When it happens
Trigger: Calling the transactions API with an array containing a transaction whose TemplateDocTreePlanID is set alongside another transaction; or including two plan-bearing transactions in one request; performTransactions invokes this during commit.
Common situations: Client-side code batching a template-doc-tree apply with unrelated edits in one request; retry logic re-sending a plan transaction after a partial failure, duplicating it in the queue; plugin code constructing multiple transactions but submitting them together.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- template document tree plans cannot be attached to replay tr
- template document tree plan requires reversible parent opera
- template document tree transaction contains a reserved opera
- attribute view not found
- ErrSpecTooNew
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/1c50221bf35550a2.
Report an issue: GitHub.