{"record":{"id":"a766ff0de9efdad4","repo":"siyuan-note/siyuan","slug":"conf-language-396","errorCode":null,"errorMessage":"Conf.Language(396)","messagePattern":"Conf\\.Language\\(396\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/pinned_docs.go","lineNumber":186,"sourceCode":"\tif err != nil {\n\t\treturn err\n\t}\n\tselected := map[string]bool{}\n\trefs := []pinnedDocRef{}\n\tfor _, id := range ids {\n\t\tif !ast.IsNodeIDPattern(id) {\n\t\t\treturn fmt.Errorf(\"invalid document ID [%s]\", id)\n\t\t}\n\t\tif selected[id] {\n\t\t\tcontinue\n\t\t}\n\t\tselected[id] = true\n\t\tif action == \"unpin\" {\n\t\t\tcontinue\n\t\t}\n\t\tbt := treenode.GetBlockTree(id)\n\t\tif bt != nil && IsEncryptedBox(bt.BoxID) {\n\t\t\treturn fmt.Errorf(\"%s\", Conf.Language(396))\n\t\t}\n\t\tif !isPinnableDocument(bt) {\n\t\t\treturn fmt.Errorf(\"document [%s] cannot be pinned\", id)\n\t\t}\n\t\tbox := Conf.Box(bt.BoxID)\n\t\tif box == nil || box.Stat(bt.Path) == nil {\n\t\t\treturn fmt.Errorf(\"document [%s] is unavailable\", id)\n\t\t}\n\t\tial := box.docIAL(bt.Path)\n\t\tif ial == nil {\n\t\t\treturn fmt.Errorf(\"cannot read pinned document [%s]\", id)\n\t\t}\n\t\tif id != bt.BoxID && ial[DocHiddenAttr] == \"true\" {\n\t\t\treturn fmt.Errorf(\"document [%s] cannot be pinned\", id)\n\t\t}\n\t\trefs = append(refs, pinnedDocRef{ID: id, Notebook: bt.BoxID})\n\t}\n\tif action == \"pin\" && selected[targetID] {","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/pinned_docs.go#L168-L204","documentation":"When pinning, UpdatePinnedDocs resolves each ID via treenode.GetBlockTree and refuses to pin documents that live in an encrypted notebook. Instead of a hard-coded message it returns the localized user-facing string Conf.Language(396), which explains that encrypted notebooks do not support this operation. This keeps encrypted-notebook restrictions consistent with other kernel operations.","triggerScenarios":"Calling UpdatePinnedDocs with action=\"pin\" and an ids entry whose block-tree record points into a notebook where IsEncryptedBox(bt.BoxID) is true (an encrypted notebook). Unpinning skips this check entirely.","commonSituations":"User or plugin tries to pin a document inside an encrypted notebook via the pin UI, API, or a script; encrypted-notebook content is being manipulated by an external tool that ignores the restriction.","solutions":["Move the document to a non-encrypted notebook before pinning it.","Skip pinning documents whose notebook is encrypted; filter by IsEncryptedBox before calling.","Handle the localized message in the client UI as an informational restriction rather than retrying."],"exampleFix":"// before\nmodel.UpdatePinnedDocs([]string{docID}, \"pin\", \"\", false)\n// after\nif bt := treenode.GetBlockTree(docID); bt == nil || model.IsEncryptedBox(bt.BoxID) {\n    return // do not attempt to pin encrypted-notebook docs\n}\nmodel.UpdatePinnedDocs([]string{docID}, \"pin\", \"\", false)","handlingStrategy":"validation","validationCode":"bt := treenode.GetBlockTree(docID)\nif bt != nil && model.IsEncryptedBox(bt.BoxID) {\n    return errors.New(\"refusing to pin document in encrypted notebook\")\n}","typeGuard":"func isPlainBox(boxID string) bool { return !model.IsEncryptedBox(boxID) }","tryCatchPattern":"if err := model.UpdatePinnedDocs(ids, \"pin\", \"\", false); err != nil {\n    if err.Error() == model.Conf.Language(396) {\n        // surface encrypted-notebook restriction to the user\n    }\n}","preventionTips":["Check IsEncryptedBox on the notebook before any pin operation","Skip encrypted notebooks when batch-pinning","Educate users that encrypted notebooks do not support pinned entries","Keep the localized message (Language key 396) handled as a restriction, not a bug"],"tags":["encrypted-notebook","pinned-docs","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}