{"record":{"id":"32fa6a84e8998688","repo":"siyuan-note/siyuan","slug":"encrypted-notebooks-do-not-support-this-operation","errorCode":null,"errorMessage":"Encrypted notebooks do not support this operation","messagePattern":"Encrypted notebooks do not support this operation","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/blockial.go","lineNumber":367,"sourceCode":"\tIncSync()\n\tnewAttrs := parse.IAL2Map(node.KramdownIAL)\n\tcache.PutBlockIALInBox(node.ID, tree.Box, newAttrs)\n\tif oldAttrs[DocSortModeAttr] != newAttrs[DocSortModeAttr] && ast.NodeDocument == node.Type {\n\t\tcache.RemoveDocIALInBox(tree.Path, tree.Box)\n\t\tcache.PutDocIALInBox(tree.Path, tree.Box, newAttrs)\n\t}\n\tpushBlockAttrs(oldAttrs, node)\n\tpushDocSortModeChanged(oldAttrs, node, tree)\n\treturn\n}\n\nfunc setNodeAttrs0(node *ast.Node, nameValues map[string]string, boxID string) (oldAttrs map[string]string, err error) {\n\t// 加密笔记本不支持书签和标签（依赖全局 SQLite 聚合，加密笔记本是孤岛）\n\tif IsEncryptedBox(boxID) && boxID != \"\" {\n\t\tfor name := range nameValues {\n\t\t\tswitch strings.ToLower(name) {\n\t\t\tcase \"bookmark\", \"tags\":\n\t\t\t\terr = errors.New(Conf.Language(313))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\toldAttrs = parse.IAL2Map(node.KramdownIAL)\n\tnewAttrsUnEsc := parse.IAL2MapUnEsc(node.KramdownIAL)\n\tfor name := range nameValues {\n\t\tif \"fold\" == strings.ToLower(name) {\n\t\t\tdelete(newAttrsUnEsc, \"heading-fold\")\n\t\t\tbreak\n\t\t}\n\t}\n\n\tfor name, value := range nameValues {\n\t\tvalue = util.RemoveInvalidRetainCtrl(value)\n\t\tvalue = strings.TrimSpace(value)\n\t\tlowerName := strings.ToLower(name)\n\t\t// 转换为小写再验证属性名","sourceCodeStart":349,"sourceCodeEnd":385,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/model/blockial.go#L349-L385","documentation":"kernel/model/blockial.go:367, from setNodeAttrs0 (language key 313): if the target block lives in an encrypted notebook (IsEncryptedBox) and the requested attributes include 'bookmark' or 'tags' (case-insensitive), the write is refused with 'Encrypted notebooks do not support this operation'. Rationale (per the code comment): bookmarks and tags rely on the global SQLite aggregation, but an encrypted notebook is an isolated island, so global attr features are unavailable there.","triggerScenarios":"POST /api/attr/setBlockAttrs or /api/attr/batchSetBlockAttrs with attrs {\"bookmark\": ...} or {\"tags\": ...} for a block whose notebook is encrypted (locked with a passcode); setting a bookmark or tag from the UI or API inside an encrypted notebook.","commonSituations":"Automation that applies uniform bookmarks/tags across all notebooks without checking notebook encryption state; users enabling notebook encryption later, breaking existing tagging flows.","solutions":["Skip bookmark/tags attributes for blocks in encrypted notebooks; use custom-prefixed attrs (e.g. custom-mytag) which are allowed, since they stay document-local.","Un-encrypt the notebook if global bookmark/tag features are required there.","Detect encrypted notebooks up front via the notebook list API and branch your logic."],"exampleFix":"// before\nawait setBlockAttrs(id, { tags: \"#project\" }); // id is in an encrypted notebook\n\n// after\nawait setBlockAttrs(id, { \"custom-mytag\": \"project\" }); // document-local attr is allowed","handlingStrategy":"validation","validationCode":"// Skip global-only attrs when the target notebook is encrypted\nconst boxes = (await (await fetch(\"/api/notebook/lsNotebooks\", { method: \"POST\" })).json()).data?.notebooks ?? [];\nconst encrypted = new Set(boxes.filter((b: any) => b.enclosed ?? b.encrypted).map((b: any) => b.id));\n// for the target's box (from /api/query root_box_id), filter:\nfunction safeAttrs(boxID: string, attrs: Record<string, string>) {\n  if (!encrypted.has(boxID)) return attrs;\n  return Object.fromEntries(Object.entries(attrs)\n    .filter(([k]) => !['bookmark', 'tags'].includes(k.toLowerCase())));\n}","typeGuard":"const isEncryptedNotebook = (box: { enclosed?: boolean } | undefined) => !!box?.enclosed;","tryCatchPattern":"try {\n  await setBlockAttrs(id, attrs);\n} catch (e) {\n  if (/Encrypted notebooks do not support this operation/.test(e.message)) {\n    const { bookmark, tags, ...rest } = attrs;\n    await setBlockAttrs(id, rest); // apply what is allowed\n  } else throw e;\n}","preventionTips":["Treat bookmark/tags as global features; never assume they work everywhere.","Key automation metadata off custom-* attrs, which work in all notebooks.","The name check is case-insensitive ('Tags' is rejected too)."],"tags":["siyuan","encrypted-notebook","block-attrs","policy","api"],"backgroundTag":"unsupported-operation-context","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}