siyuan-note/siyuan · error
template document tree plan is invalid or has expired
Error message
template document tree plan is invalid or has expired
What it means
After the plan is found in the map, it is type-asserted to *templateDocTreePlan and checked for ID match and expiry (time.Now().After(plan.expiresAt)). This error means the stored entry is corrupt or of the wrong type, the ID does not match the entry, or the plan was still in the map when its TTL elapsed (e.g. the AfterFunc deletion raced with the attach).
Source
Thrown at kernel/model/template_doc_tree.go:486
}
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) {
return false, errors.New("template document tree plan is invalid or has expired")
}
if !transactionTargetsTemplateRoot(target, plan.rootID, plan.boxID) {
return false, errors.New("template document tree plan does not match the edited document")
}
rootTree, loadErr := LoadTreeByBlockID(plan.rootID)
if nil != loadErr || nil == rootTree || rootTree.Box != plan.boxID || rootTree.Path != plan.rootPath ||
rootTree.HPath != plan.rootHPath {
return false, errors.New("the document used to render the template has changed")
}
target.templateDocTreeRootSnapshot = rootTree
box := Conf.Box(plan.boxID)
if nil == box {
return false, ErrBoxNotFound
}
for _, tree := range plan.trees {
if nil == tree || nil == tree.Root || tree.ID != tree.Root.ID || tree.Box != plan.boxID || box.Exist(tree.Path) {
return false, errors.New("template document tree plan contains an invalid document snapshot")
}View on GitHub (pinned to 8641553a1f)
Solutions
- Re-render the template to create a fresh plan and attach it promptly (well inside the TTL)
- Do not mutate TemplateDocTreePlanID between plan creation and transaction submission
- Check for races: ensure only one goroutine/transaction consumes a given plan ID
- If this recurs, file a bug with kernel logs — a wrong-typed map value is an internal invariant violation
Example fix
// before
// submitting 10 minutes after rendering; templateDocTreePlanTTL already elapsed
// after
planID, _ := renderTemplateDocTree(...)
go func() { time.Sleep(jitter); tx.TemplateDocTreePlanID = planID; performTransactions([]*Transaction{tx}) }() // submit immediately after rendering Defensive patterns
Strategy: retry
Validate before calling
if time.Now().After(planExpiresAt) { re-render the plan before attaching } Try / catch
if err != nil && strings.Contains(err.Error(), "plan is invalid or has expired") { tx.TemplateDocTreePlanID = reRenderPlan(); retry } Prevention
- Submit the transaction immediately after rendering, never near the TTL boundary
- Use one consumer goroutine per plan to avoid consume/expiry races
- Never mutate the plan ID between creation and submission
- Treat recurring wrong-type hits as a kernel bug and report with logs
When it happens
Trigger: Transaction submitted within TTL but the AfterFunc timer fired concurrently and the map still holds the entry; internal map corruption/type mismatch; plan.expiresAt already passed at attach time; planID mutated on the client so it no longer equals plan.id.
Common situations: Attaching a plan right at the TTL boundary; clock skew if the plan was created on a machine with a different clock (rare, single-process); bugs in custom code that stores foreign values into templateDocTreePlans.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- template document tree plan is missing or has expired
- template document tree plan does not match the edited docume
- the document used to render the template has changed
- attribute view [%s] view [%s] not found
- attribute view [%s] has no available view: %w
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/1211bfb2604fb012.
Report an issue: GitHub.