siyuan-note/siyuan · error
Encrypted notebooks do not support this operation
Error message
Encrypted notebooks do not support this operation
What it means
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.
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.
Example fix
// before
await setBlockAttrs(id, { tags: "#project" }); // id is in an encrypted notebook
// after
await setBlockAttrs(id, { "custom-mytag": "project" }); // document-local attr is allowed Defensive patterns
Strategy: validation
Validate before calling
// Skip global-only attrs when the target notebook is encrypted
const boxes = (await (await fetch("/api/notebook/lsNotebooks", { method: "POST" })).json()).data?.notebooks ?? [];
const encrypted = new Set(boxes.filter((b: any) => b.enclosed ?? b.encrypted).map((b: any) => b.id));
// for the target's box (from /api/query root_box_id), filter:
function safeAttrs(boxID: string, attrs: Record<string, string>) {
if (!encrypted.has(boxID)) return attrs;
return Object.fromEntries(Object.entries(attrs)
.filter(([k]) => !['bookmark', 'tags'].includes(k.toLowerCase())));
} Type guard
const isEncryptedNotebook = (box: { enclosed?: boolean } | undefined) => !!box?.enclosed; Try / catch
try {
await setBlockAttrs(id, attrs);
} catch (e) {
if (/Encrypted notebooks do not support this operation/.test(e.message)) {
const { bookmark, tags, ...rest } = attrs;
await setBlockAttrs(id, rest); // apply what is allowed
} else throw e;
} Prevention
- 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).
When it happens
Trigger: 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.
Common situations: Automation that applies uniform bookmarks/tags across all notebooks without checking notebook encryption state; users enabling notebook encryption later, breaking existing tagging flows.
Related errors
- Conf.Language(390)
- Content block with id
- Encrypted notebooks do not support this operation
- get block failed
- Please unlock the encrypted notebook first
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/32fa6a84e8998688.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/blockial.go:367
IncSync()
newAttrs := parse.IAL2Map(node.KramdownIAL)
cache.PutBlockIALInBox(node.ID, tree.Box, newAttrs)
if oldAttrs[DocSortModeAttr] != newAttrs[DocSortModeAttr] && ast.NodeDocument == node.Type {
cache.RemoveDocIALInBox(tree.Path, tree.Box)
cache.PutDocIALInBox(tree.Path, tree.Box, newAttrs)
}
pushBlockAttrs(oldAttrs, node)
pushDocSortModeChanged(oldAttrs, node, tree)
return
}
func setNodeAttrs0(node *ast.Node, nameValues map[string]string, boxID string) (oldAttrs map[string]string, err error) {
// 加密笔记本不支持书签和标签(依赖全局 SQLite 聚合,加密笔记本是孤岛)
if IsEncryptedBox(boxID) && boxID != "" {
for name := range nameValues {
switch strings.ToLower(name) {
case "bookmark", "tags":
err = errors.New(Conf.Language(313))
return
}
}
}
oldAttrs = parse.IAL2Map(node.KramdownIAL)
newAttrsUnEsc := parse.IAL2MapUnEsc(node.KramdownIAL)
for name := range nameValues {
if "fold" == strings.ToLower(name) {
delete(newAttrsUnEsc, "heading-fold")
break
}
}
for name, value := range nameValues {
value = util.RemoveInvalidRetainCtrl(value)
value = strings.TrimSpace(value)
lowerName := strings.ToLower(name)
// 转换为小写再验证属性名View on GitHub (pinned to afa823b6b4)