siyuan-note/siyuan · error
document [ ] cannot be pinned
Error message
document [%s] cannot be pinned
What it means
isPinnableDocument only accepts documents that are notebook roots or regular top-level documents: the block-tree record must exist, tree.ID == tree.RootID, tree.Type == "d", and the notebook must not be encrypted. If the ID resolves to a non-document block, a child document without an allowed tree shape, or an unknown/unindexed block (bt == nil), the pin is rejected with this error.
Solutions
- Pass the document root ID, not a child block ID.
- Wait for indexing to finish (or trigger a reindex) so GetBlockTree can resolve the ID.
- Remove the custom-doc-hidden attribute or unhide the document before pinning.
- Verify the document still exists and was not replaced by a new ID.
Example fix
// before
model.UpdatePinnedDocs([]string{blockID}, "pin", "", false)
// after
bt := treenode.GetBlockTree(blockID)
if bt == nil || bt.ID != bt.RootID || bt.Type != "d" {
return fmt.Errorf("%s is not a pinnable document", blockID)
}
model.UpdatePinnedDocs([]string{blockID}, "pin", "", false) Defensive patterns
Strategy: validation
Validate before calling
func pinnable(id string) bool {
bt := treenode.GetBlockTree(id)
return bt != nil && bt.ID == bt.RootID && bt.Type == "d" && !model.IsEncryptedBox(bt.BoxID)
} Type guard
func isDocRoot(bt *treenode.BlockTree) bool { return bt != nil && bt.ID == bt.RootID && bt.Type == "d" } Try / catch
if err := model.UpdatePinnedDocs(ids, "pin", "", false); err != nil {
if strings.Contains(err.Error(), "cannot be pinned") {
// re-resolve IDs, drop non-document roots, retry
}
} Prevention
- Pass document root IDs, never child block IDs
- Confirm GetBlockTree resolves the ID after import/sync before pinning
- Unhide documents (custom-doc-hidden != "true") before pinning
- Check index build completion before pinning freshly imported docs
When it happens
Trigger: Pinning an ID that resolves to a block tree entry where isPinnableDocument returns false: a non-root block ID (heading, paragraph), a bt == nil (not in block tree), or a doc that is hidden while also being the notebook root ID form (see the DocHiddenAttr check which reuses this message). Also triggered when the id equals bt.BoxID but ial[DocHiddenAttr]=="true" at line 200.
Common situations: Plugin passes a paragraph or heading block ID instead of a document ID; block tree index is not yet built after import/sync so GetBlockTree returns nil; user tries to pin a hidden document; stale ID after the document was deleted and re-created.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- block [ ] is not a document that can declare a child…
- --id is required
- invalid block structure: block node is nil
- invalid block structure
- invalid block structure
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/cfe86f1df53b8d54.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/pinned_docs.go:189
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)
}
ial := box.docIAL(bt.Path)
if ial == nil {
return fmt.Errorf("cannot read pinned document [%s]", id)
}
if id != bt.BoxID && ial[DocHiddenAttr] == "true" {
return fmt.Errorf("document [%s] cannot be pinned", id)
}
refs = append(refs, pinnedDocRef{ID: id, Notebook: bt.BoxID})
}
if action == "pin" && selected[targetID] {
return nil
}
remaining := []pinnedDocRef{}View on GitHub (pinned to 9f775e8a12)