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
- 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)
- Remove duplicate entries so each document ID appears at most once
- Re-pin the documents from the UI, which rewrites the file with valid IDs
- 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
- Always copy node IDs from SiYuan (copy block ID) rather than typing them
- Deduplicate IDs before persisting pin lists
- Never leave id or notebook empty in pinned-docs.json
- Reset the file if unsure; pins are trivially recreated in the UI
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
- Conf.Language(142)
- invalid session data
- unsupported pinned documents version
- %w: %v
- all asset mappings failed
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)