{"record":{"id":"6b232af8e87b2e9c","repo":"siyuan-note/siyuan","slug":"invalid-sy-base-name-s-stem-is-not-a-node-id","errorCode":null,"errorMessage":"invalid .sy base name [%s]: stem is not a node ID","messagePattern":"invalid \\.sy base name \\[(.+?)\\]: stem is not a node ID","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/filesys/crypto_hook.go","lineNumber":116,"sourceCode":"\t}\n\treturn util.DecryptWithAAD(fileKey, data, []byte(aad))\n}\n\n// SyObjectBase 从 box 内相对路径提取稳定文件基名并校验合法性。\n// 接受形如 <rootID>.sy 的基名：扩展名必须是 .sy，且 stem 是合法节点 ID。\n// 非法扩展名或非节点 ID 模式返回错误，避免把任意路径当 AAD 绑定物产生不可解密的数据。\n// 由 filesys、model 历史查看/回滚、import 等所有 .sy 加解密路径共同使用，保证 AAD 一致。\nfunc SyObjectBase(relativePath string) (string, error) {\n\tbase := relativePath\n\tif idx := strings.LastIndexAny(relativePath, \"/\\\\\"); idx >= 0 {\n\t\tbase = relativePath[idx+1:]\n\t}\n\tif !strings.HasSuffix(base, \".sy\") {\n\t\treturn \"\", fmt.Errorf(\"invalid .sy base name [%s]: must end with .sy\", base)\n\t}\n\tstem := strings.TrimSuffix(base, \".sy\")\n\tif !ast.IsNodeIDPattern(stem) {\n\t\treturn \"\", fmt.Errorf(\"invalid .sy base name [%s]: stem is not a node ID\", base)\n\t}\n\treturn base, nil\n}\n\n// SyAAD 构造 .sy 密文的 AAD：siyuan:file:<boxID>:<稳定文件基名>。\n// 父目录不进 AAD——同 box 内文件名不变的移动允许原样 Rename 密文，内容/box/类型/对象 ID 仍受认证。\nfunc SyAAD(boxID, relativePath string) (string, error) {\n\tbase, err := SyObjectBase(relativePath)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn \"siyuan:file:\" + boxID + \":\" + base, nil\n}\n\n// encryptedBox 判断 boxID 是否为已解锁的加密 box，供 filesys 内部分流（如静默修正禁用）。\n// 通过 DEKProvider 探测：返回非 nil dek 即加密且已解锁。\nfunc encryptedBox(boxID string) bool {\n\tif DEKProvider == nil {","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/filesys/crypto_hook.go#L98-L134","documentation":"After confirming the \".sy\" suffix, SyObjectBase requires the stem to match ast.IsNodeIDPattern, i.e. a SiYuan node ID like \"20260101120000-abcdefg\". Because the AAD binds the ciphertext to the object ID, only node-ID-named documents qualify. The error means the suffix was fine but the stem is not a node ID.","triggerScenarios":"Calling SyObjectBase with names like \"notes.sy\", \"Untitled.sy\", \"123.sy\", or a legacy/custom file name whose stem is not the yyyyMMddHHmmss-7hex node ID format.","commonSituations":"Imported or renamed documents that kept a human-readable file name; tests using fake .sy names; constructing document paths manually instead of from the tree ID.","solutions":["Use the document's real node ID file name (look it up via the tree/block ID).","Rename the file to its node ID, e.g. 20260101120000-abcdefg.sy, if it is genuinely a document.","If the stem format is unexpected, regenerate the document ID and rebuild the .sy file through the normal write path."],"exampleFix":"// before\nSyObjectBase(\"notes.sy\")\n// after\nSyObjectBase(\"20260101120000-abcdefg.sy\")","handlingStrategy":"validation","validationCode":"const re = /^\\d{14}-[0-9a-f]{7}\\.sy$/;\nif (!re.test(baseName)) throw new Error(\"base name is not a node ID .sy file\");","typeGuard":null,"tryCatchPattern":"base, err := filesys.SyObjectBase(relPath)\nif err != nil {\n    // fall back to ID lookup before crypto operations\n}","preventionTips":["Derive document file names from block/tree IDs, never from titles.","Do not rename .sy files to human-readable names.","Validate the yyyyMMddHHmmss-7hex ID pattern before calling."],"tags":["validation","identifier","encryption"],"backgroundTag":"invalid-identifier-format","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"}