siyuan-note/siyuan · error

invalid pinned document

Error message

invalid pinned document [%s]

What it means

readPinnedDocs throws "invalid pinned document [%s]" when a stored pinned entry has an ID or notebook that is not a valid SiYuan node-ID pattern, or when the same document ID appears twice in the docs array. It prevents malformed pin references from leaking into the file tree.

Solutions

  1. Open data/storage/pinned-docs.json and fix/remove entries whose id or notebook is not in the 20-char node-ID form (e.g. 20240101120000-abcdefg)
  2. Remove duplicate entries so each document ID appears at most once
  3. Re-pin the documents from the UI, which rewrites the file with valid IDs
  4. Delete pinned-docs.json to reset if unsure; pins are easily recreated

Example fix

// before
{"version":1,"docs":[{"id":"my-doc","notebook":""}]}
// after
{"version":1,"docs":[{"id":"20240101120000-abc1234","notebook":"20240101120000-box5678"}]}
Defensive patterns

Strategy: validation

Validate before calling

// Validate node-ID shape before writing pin refs
re := regexp.MustCompile(`^\d{14}-[0-9a-z]{7}$`)
valid := re.MatchString(id) && re.MatchString(notebookID)

Try / catch

docs, err := GetPinnedDocs()
if err != nil && strings.Contains(err.Error(), "invalid pinned document") {
	// repair: rewrite the file keeping only entries with valid unique IDs
}

Prevention

When it happens

Trigger: Loading pinned docs where any entry's id or notebook fails ast.IsNodeIDPattern (e.g. empty string, random text, wrong format) or a duplicate ID exists in pinned-docs.json.

Common situations: Hand-editing pinned-docs.json with wrong IDs; corruption from a bad sync merge; entries left over from a defective older version; copying entries and forgetting to change the ID.

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/32a31e0d5ba65dda. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/pinned_docs.go:62

	ret = pinnedDocsStorage{Version: 1, Docs: []pinnedDocRef{}}
	data, err := filelock.ReadFile(filepath.Join(util.DataDir, "storage", "pinned-docs.json"))
	if os.IsNotExist(err) {
		return ret, nil
	}
	if err != nil {
		return
	}
	ret = pinnedDocsStorage{}
	if err = json.Unmarshal(data, &ret); err != nil {
		return
	}
	if ret.Version != 1 || ret.Docs == nil {
		return ret, fmt.Errorf("unsupported pinned documents version [%d]", ret.Version)
	}
	seen := map[string]bool{}
	for _, doc := range ret.Docs {
		if !ast.IsNodeIDPattern(doc.ID) || !ast.IsNodeIDPattern(doc.Notebook) || seen[doc.ID] {
			return ret, fmt.Errorf("invalid pinned document [%s]", doc.ID)
		}
		seen[doc.ID] = true
	}
	return
}

func writePinnedDocs(data pinnedDocsStorage) error {
	encoded, err := json.Marshal(data)
	if err != nil {
		return err
	}
	dir := filepath.Join(util.DataDir, "storage")
	if err = os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	if err = filelock.WriteFile(filepath.Join(dir, "pinned-docs.json"), encoded); err != nil {
		return err
	}

View on GitHub (pinned to 9f775e8a12)