siyuan-note/siyuan · error
template document tree plans cannot be attached to replay tr
Error message
template document tree plans cannot be attached to replay transactions
What it means
AttachTemplateDocTreePlans rejects plans attached to transactions marked isReplay. Replay transactions are internal re-executions (e.g. after undo/redo or sync replay); attaching a document-tree plan there would re-run document creation outside the intended user-initiated flow. The plan must ride on a fresh, non-replay transaction.
Source
Thrown at kernel/model/template_doc_tree.go:464
}
// 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")
}
}
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")View on GitHub (pinned to 8641553a1f)
Solutions
- Clear TemplateDocTreePlanID from a transaction before re-submitting or replaying it (AttachTemplateDocTreePlans already does this on the target before persisting)
- Re-issue the template document tree creation as a fresh transaction instead of replaying the old one
- If replaying captured transactions, strip plan metadata first and re-attach via the normal render flow
- Exclude template-plan transactions from any custom sync/replay pipeline
Example fix
// before: replaying a stored transaction as-is
replayTx := storedTx
performTransactions([]*Transaction{replayTx})
// after: strip plan metadata before replay
storedTx.TemplateDocTreePlanID = ""
storedTx.isReplay = false
performTransactions([]*Transaction{storedTx}) Defensive patterns
Strategy: validation
Validate before calling
// Go: refuse to replay transactions that still carry plan metadata
if tx.isReplay && tx.TemplateDocTreePlanID != "" {
return errors.New("strip TemplateDocTreePlanID before replay")
} Try / catch
if _, err := AttachTemplateDocTreePlans([]*Transaction{tx}); err != nil && strings.Contains(err.Error(), "replay") {
tx.TemplateDocTreePlanID = ""
return performTransactions([]*Transaction{tx})
} Prevention
- Clear TemplateDocTreePlanID before storing or re-submitting transactions
- Exclude template-plan transactions from undo/redo replay capture
- Re-create document trees via the normal template flow instead of replaying old transactions
- Keep plan metadata out of any synced or logged transaction payloads
When it happens
Trigger: A transaction carrying TemplateDocTreePlanID being pushed through the replay path of performTransactions — for example when redo re-executes a stored transaction, or sync/replay machinery re-processes a transaction that still has a plan ID attached.
Common situations: Custom tooling that stores and re-submits captured transactions including their plan IDs; redoing a template transaction where the plan ID was not cleared before replay; sync engines replaying remote transaction logs that contain plan markers.
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
- a document tree plan must be applied in a single transaction
- 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/152dafca90255f38.
Report an issue: GitHub.