{"record":{"id":"137d2d4384429836","repo":"siyuan-note/siyuan","slug":"encrypted-repository-data-is-missing-notebook-cont","errorCode":null,"errorMessage":"encrypted repository data is missing notebook context","messagePattern":"encrypted repository data is missing notebook context","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/repository.go","lineNumber":724,"sourceCode":"\t\t}\n\n\t\ttitle = tree.Root.IALAttr(\"title\")\n\t\trootID = tree.Root.ID\n\t}\n\treturn\n}\n\n// decryptRepoDataIfNeeded 判断仓库数据是否属于加密笔记本，如果是则按路径类型分流解密。\n// file.Path 格式：/<boxID>/...\n// .sy → DecryptFile，assets/* → DecryptAsset，storage/av/*.json → av.DecryptAVData。\n// 密文缺少有效路径上下文、笔记本未解锁或认证失败时返回错误，不允许调用方按明文继续处理。\nfunc decryptRepoDataIfNeeded(data []byte, filePath string) ([]byte, error) {\n\trelPath := strings.TrimPrefix(filePath, \"/\")\n\tparts := strings.SplitN(relPath, \"/\", 2)\n\tencryptedPayload := util.IsCiphertext(data) || bytes.HasPrefix(data, encryptedAssetMagic)\n\tif len(parts) < 2 || !ast.IsNodeIDPattern(parts[0]) {\n\t\tif encryptedPayload {\n\t\t\treturn nil, errors.New(\"encrypted repository data is missing notebook context\")\n\t\t}\n\t\treturn data, nil\n\t}\n\tboxID := parts[0]\n\tif !IsEncryptedBox(boxID) {\n\t\tif encryptedPayload {\n\t\t\treturn nil, fmt.Errorf(\"encrypted repository data has no matching notebook [%s]\", boxID)\n\t\t}\n\t\treturn data, nil\n\t}\n\t// 持读锁，防止 LockBox 在解密期间清 DEK/缓存\n\tHoldBoxReadLock(boxID)\n\tdefer ReleaseBoxReadLock(boxID)\n\tdek, err := GetDEKIfUnlocked(boxID)\n\tif err != nil {\n\t\treturn nil, errors.New(Conf.Language(314))\n\t}\n\tboxRelPath := parts[1]","sourceCodeStart":706,"sourceCodeEnd":742,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/repository.go#L706-L742","documentation":"decryptRepoDataIfNeeded rejects encrypted payloads (ciphertext or encrypted-asset magic) when the file path cannot be split into '<boxID>/<relpath>' with a valid node-ID boxID. Without a well-formed notebook path prefix there is no way to select the right notebook key, so it returns 'encrypted repository data is missing notebook context'.","triggerScenarios":"Calling any path-based repo reader (GetRepoFile, OpenRepoSnapshotFile, RollbackRepoSnapshotFile, ExportRepoFile, parseTitleInSnapshot) with an encrypted payload whose path has no directory prefix or whose first segment is not a SiYuan node-ID pattern (e.g. root-level files like 'index.json' that are somehow encrypted).","commonSituations":"Snapshots containing top-level non-document files that were encrypted by mistake; tooling that strips the first path component before calling the API; manually constructed repo paths that omit the notebook-ID segment; legacy snapshots from format transitions.","solutions":["Pass the full repo-relative path including the notebook-ID first segment to the repo API","Re-create or re-index the affected snapshot so paths carry the boxID prefix","If the encrypted payload legitimately has no notebook context, this data is unreadable by design — remove it from the repo or re-store it unencrypted","Verify callers don't TrimPrefix or rewrite the path before invoking repo file APIs"],"exampleFix":"// before: path lost the notebook prefix\nawait fetchPost('/api/repo/getRepoFile', { path: 'assets/foo.png' });\n// after: keep the boxID segment\nawait fetchPost('/api/repo/getRepoFile', { path: '20240101120000-abcdefg/assets/foo.png' });","handlingStrategy":"validation","validationCode":"const rel = repoPath.replace(/^\\//, '');\nconst m = rel.match(/^([0-9]{14}-[0-9a-z]{7})\\//);\nif (!m) throw new Error('Encrypted repo path needs a notebook-ID prefix: ' + repoPath);","typeGuard":"const hasBoxPrefix = (p) => /^\\/?[0-9]{14}-[0-9a-z]{7}\\//.test(p);","tryCatchPattern":"try {\n  return await fetchPost('/api/repo/getRepoFile', { path: repoPath });\n} catch (e) {\n  if (String(e).includes('missing notebook context')) logUnreadableEntry(repoPath);\n  else throw e;\n}","preventionTips":["Never strip the leading notebook-ID segment from repo paths","Use paths exactly as returned by repo listing APIs","Re-index after unusual workspace manipulations to rebuild snapshot paths","Treat root-level encrypted entries as corruption to be re-snapshotted"],"tags":["repository","encryption","path","notebook"],"backgroundTag":"invalid-argument-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"}