siyuan-note/siyuan · error

invalid document ID [ ]

Error message

invalid document ID [%s]

What it means

UpdatePinnedDocs validates every document ID passed by the client before changing the pinned-documents list. ast.IsNodeIDPattern requires a canonical 22-character SiYuan node ID (base32-style pattern used for .sy file names). If any id in the ids slice is empty, truncated, or otherwise not a well-formed node ID, the whole update is rejected with this error before any storage mutation.

Solutions

  1. Fix the caller to pass real node IDs: fetch them from GetPinnedDocs / the doc tree rather than constructing strings.
  2. Validate each id against ast.IsNodeIDPattern client-side before sending the request.
  3. Check for empty strings or whitespace in the ids array and remove them.
  4. If IDs came from a manually edited pinned-docs storage file, restore IDs from the document tree or let maintainPinnedDocs clean them up.

Example fix

// before
UpdatePinnedDocs([]string{doc.Title}, "pin", "", false)
// after
UpdatePinnedDocs([]string{doc.ID}, "pin", "", false)
Defensive patterns

Strategy: validation

Validate before calling

func canPin(ids []string) bool {
    for _, id := range ids {
        if !ast.IsNodeIDPattern(id) { return false }
    }
    return len(ids) > 0
}

Type guard

func isValidDocID(id string) bool { return ast.IsNodeIDPattern(id) }

Try / catch

if err := model.UpdatePinnedDocs(ids, "pin", "", false); err != nil {
    if strings.HasPrefix(err.Error(), "invalid document ID") {
        // refresh IDs from GetPinnedDocs and retry once
    }
}

Prevention

When it happens

Trigger: Calling UpdatePinnedDocs (or the /api/filetree/updatePinnedDocs-style HTTP endpoint) with an ids array containing a malformed string: an empty id, a title pasted instead of an ID, a hand-constructed or truncated ID, or an ID copied from another system.

Common situations: Plugins or scripts building the request from stale data; clients that pass document titles or paths instead of block IDs; IDs corrupted by manual editing of the storage file or by an older client version that used a different ID format.

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@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/5ff0eba53fe6922b. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/pinned_docs.go:175

// 根层顺序独立于源文档顺序,按相对位置更新以保留其他窗口新增的入口。
func UpdatePinnedDocs(ids []string, action, targetID string, after bool) error {
	if len(ids) == 0 {
		return fmt.Errorf("document IDs are required")
	}
	if action != "pin" && action != "unpin" {
		return fmt.Errorf("invalid pinned document action")
	}
	pinnedDocsLock.Lock()
	defer pinnedDocsLock.Unlock()
	stored, err := readPinnedDocs()
	if err != nil {
		return err
	}
	selected := map[string]bool{}
	refs := []pinnedDocRef{}
	for _, id := range ids {
		if !ast.IsNodeIDPattern(id) {
			return fmt.Errorf("invalid document ID [%s]", id)
		}
		if selected[id] {
			continue
		}
		selected[id] = true
		if action == "unpin" {
			continue
		}
		bt := treenode.GetBlockTree(id)
		if bt != nil && IsEncryptedBox(bt.BoxID) {
			return fmt.Errorf("%s", Conf.Language(396))
		}
		if !isPinnableDocument(bt) {
			return fmt.Errorf("document [%s] cannot be pinned", id)
		}
		box := Conf.Box(bt.BoxID)
		if box == nil || box.Stat(bt.Path) == nil {
			return fmt.Errorf("document [%s] is unavailable", id)

View on GitHub (pinned to 9f775e8a12)