siyuan-note/siyuan · error

invalid id

Error message

invalid id

What it means

Sentinel error model.ErrInvalidID (kernel/model/tree.go:208). It flags identifiers that fail format validation rather than lookup - the attribute view render path returns it for malformed av/block ids (attribute_view_render.go:394, 1047, 1083, 1124, 1189), and kernel tests assert it for invalid attribute view inputs. Unlike ErrBlockNotFound/ErrTreeNotFound, the id never gets as far as a blocktree lookup: its shape is wrong.

Solutions

  1. Use ids exactly as returned by the kernel (block info queries, av search/list endpoints)
  2. Validate the id shape client-side before calling (see type guard)
  3. Match with errors.Is(err, model.ErrInvalidID) to distinguish 'bad format' from 'not found' in error handling
Defensive patterns

Strategy: type-guard

Validate before calling

// Reject malformed ids client-side before attribute-view calls:
var idRe = regexp.MustCompile(`^[0-9]{14}-[a-z0-9]{7}$`)
if !idRe.MatchString(avID) {
	return fmt.Errorf("invalid id format: %s", avID)
}

Type guard

func isValidNodeID(id string) bool {
	// SiYuan node ids: 14-digit timestamp + '-' + 7-char suffix
	matched, _ := regexp.MatchString(`^[0-9]{14}-[a-z0-9]{7}$`, id)
	return matched
}

Try / catch

if err := renderAV(avID); err != nil {
	if errors.Is(err, model.ErrInvalidID) {
		// format problem - fix the id source, retrying will not help
	}
}

Prevention

When it happens

Trigger: Feeding a self-invented, truncated, or wrongly-typed id into attribute view render/getAttributeView* APIs; passing an empty or whitespace id where a kernel-issued id is required.

Common situations: Plugins generating their own ids instead of using ids the kernel returned; copy-paste truncation; substituting a document path or av name where an id belongs.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/18fcb60917735226. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/tree.go:208

		}
	}

	ret, err = dataparser.ParseJSONWithoutFix(data, luteEngine.ParseOptions)
	if err != nil {
		logging.LogErrorf("parse json to tree [%s] failed: %s", localPath, err)
		return
	}
	return
}

var (
	ErrBoxNotFound   = errors.New("notebook not found")
	ErrBoxClosed     = errors.New("notebook closed")
	ErrBlockNotFound = errors.New("block not found")
	ErrTreeNotFound  = errors.New("tree not found")
	ErrIndexing      = errors.New("indexing")
	ErrBoxUnindexed  = errors.New("notebook unindexed")
	ErrInvalidID     = errors.New("invalid id")
)

func LoadTreeByBlockIDWithReindex(id string) (ret *parse.Tree, err error) {
	return LoadTreeByBlockIDWithReindexInBox(id, "")
}

// LoadTreeByBlockIDWithReindexInBox 与 LoadTreeByBlockIDWithReindex 一致,但按 boxID 路由 blocktree 查询。
func LoadTreeByBlockIDWithReindexInBox(id, boxID string) (ret *parse.Tree, err error) {
	if "" == id {
		logging.LogWarnf("block id is empty")
		return nil, ErrTreeNotFound
	}

	bt := treenode.GetBlockTreeInBox(id, boxID)
	if nil == bt && "" == boxID {
		// boxID 未知时(如通用打开入口),遍历所有已打开的加密笔记本查找
		for _, encBoxID := range treenode.GetOpenedEncryptedBoxIDs() {
			if encBT := treenode.GetBlockTreeInBox(id, encBoxID); nil != encBT {

View on GitHub (pinned to afa823b6b4)