siyuan-note/siyuan · error

verify created shorthand document [%s] failed

Error message

verify created shorthand document [%s] failed

What it means

After successfully loading the persisted tree, verifyShorthandDocPersisted asserts the loaded tree is non-nil and its ID equals the expected document ID. Any mismatch returns "verify created shorthand document [%s] failed" — the on-disk document does not correspond to the document that was supposed to be created (ID collision, wrong path, or ID-regeneration during creation).

Source

Thrown at kernel/model/shortcuts.go:326

	box.setSortByConf(path.Dir(bt.Path), retID)

	FlushTxQueue()
	PushCreate(box, bt.Path, nil)
	return
}

func verifyShorthandDocPersisted(bt *treenode.BlockTree, expectedID string) (err error) {
	if nil == bt {
		return fmt.Errorf("get block tree by id [%s] failed after create", expectedID)
	}
	// 清除写入缓存,确保从文件系统重新读取并验证文档。
	cache.RemoveTreeDataInBox(expectedID, bt.BoxID)
	persisted, err := loadTreeByBlockTree(bt)
	if nil != err {
		return fmt.Errorf("load created shorthand document [%s] failed: %w", expectedID, err)
	}
	if nil == persisted || persisted.ID != expectedID {
		return fmt.Errorf("verify created shorthand document [%s] failed", expectedID)
	}
	return nil
}

var consumeShorthandsLock = sync.Mutex{}
var shorthandSaveBoxUnavailableNotified bool

func isShorthandSaveBoxAvailable(box *Box) bool {
	return nil != box && !box.Closed && !box.Encrypted && !IsUserGuide(box.ID)
}

func selectShorthandSaveBox(configuredID string, boxes []*Box) *Box {
	if "" != configuredID {
		for _, box := range boxes {
			if nil != box && box.ID == configuredID && isShorthandSaveBoxAvailable(box) {
				return box
			}
		}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Re-run shorthand consumption; a fresh creation usually resolves a stale expectedID
  2. Check whether the target hPath already holds a document and move/clean it so the shorthand gets a unique path
  3. Inspect the created document's actual ID in the notebook and use it downstream instead of the precomputed one

Example fix

// before
expectedID := util.NodeIDByTime(created)
retID, err = createShorthandDocByDOM(boxID, hPath, dom, expectedID)
// after
retID, err = createShorthandDocByDOM(boxID, hPath, dom, expectedID)
if err != nil { /* handle verify failure */ }
useID := retID // use returned ID, not the precomputed expectedID
Defensive patterns

Strategy: validation

Validate before calling

// After creation, use the returned ID rather than the precomputed one
retID, err := createShorthandDocByDOM(boxID, hPath, dom, docID)
if err != nil { return err }
bt := treenode.GetBlockTree(retID) // verify with retID, not docID

Type guard

func idsMatch(persisted *treenode.BlockTree, expectedID string) bool {
    return persisted != nil && persisted.ID == expectedID
}

Try / catch

if err := verifyShorthandDocPersisted(bt, expectedID); err != nil {
    // fall back to the actual persisted ID and reconcile callers
    actual := treenode.GetBlockTree(bt.Path)
    return reconcile(actual.ID)
}

Prevention

When it happens

Trigger: loadTreeByBlockTree returns a tree whose ID differs from expectedID — e.g. the document was created under a different ID than the pre-computed shorthand timestamp ID, or the resolved path points to a different existing document.

Common situations: The shorthand save path already contains a document and creation merged/reused it; ID generation raced so expectedID is stale; a custom docID was rejected and a new one generated.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/9f505d1aab955f8f. Report an issue: GitHub.