{"record":{"id":"f9c390e75f91e57d","repo":"siyuan-note/siyuan","slug":"accessing-assets-in-encrypted-notebook-s-is-not","errorCode":null,"errorMessage":"accessing assets in encrypted notebook [%s] is not supported","messagePattern":"accessing assets in encrypted notebook \\[(.+?)\\] is not supported","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/assets.go","lineNumber":932,"sourceCode":"\tassetDirIndex := -1\n\tswitch {\n\tcase len(parts) > 1 && parts[0] == \"assets\":\n\t\tassetDirIndex = 0\n\tcase len(parts) > 2 && ast.IsNodeIDPattern(parts[0]):\n\t\tfor i := 1; i < len(parts)-1; i++ {\n\t\t\tif parts[i] == \"assets\" {\n\t\t\t\tassetDirIndex = i\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif assetDirIndex > 0 {\n\t\t\tboxConfPath := filepath.Join(util.DataDir, parts[0], \".siyuan\", \"conf.json\")\n\t\t\tif !filelock.IsExist(boxConfPath) {\n\t\t\t\terr = fmt.Errorf(\"asset path does not belong to a notebook: %s\", assetPath)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif IsEncryptedBox(parts[0]) {\n\t\t\t\terr = fmt.Errorf(\"accessing assets in encrypted notebook [%s] is not supported\", parts[0])\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\tif assetDirIndex < 0 {\n\t\terr = fmt.Errorf(\"path is not under an assets directory: %s\", assetPath)\n\t\treturn\n\t}\n\n\tassetRootParts := parts[:assetDirIndex+1]\n\tassetRoot := filepath.Join(util.DataDir, filepath.FromSlash(strings.Join(assetRootParts, \"/\")))\n\tif !gulu.File.IsSubPath(assetRoot, absPath) {\n\t\terr = fmt.Errorf(\"path is not a child of assets directory: %s\", assetPath)\n\t\treturn\n\t}\n\n\tresolvedRoot, evalErr := filepath.EvalSymlinks(assetRoot)\n\tif evalErr != nil {","sourceCodeStart":914,"sourceCodeEnd":950,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/assets.go#L914-L950","documentation":"Returned by ResolveDataAssetPath when the notebook identified by the first path segment is encrypted (IsEncryptedBox returns true). ResolveDataAssetPath deliberately refuses to resolve assets inside encrypted notebooks — encrypted assets are accessed through ReadAssetBytesInBox (which decrypts), not through this generic resolver. The notebook ID is interpolated.","triggerScenarios":"Calling ResolveDataAssetPath with a path whose notebook prefix is an encrypted notebook, e.g. \"<encBoxID>/assets/x.png?box=<encBoxID>\". This is by-design enforcement, not a transient error.","commonSituations":"Generic asset tooling (thumbnails, export, search-asset-content) tries to touch an encrypted notebook's assets without going through the encryption-aware read path.","solutions":["For encrypted-notebook assets, use ReadAssetBytesInBox(boxID, relativePath) instead, which acquires the read lock and decrypts.","Ensure the notebook is unlocked in this session before reading.","Route encrypted-asset access through APIs that are encryption-aware, not through ResolveDataAssetPath."],"exampleFix":"// before — generic resolver rejects encrypted notebook\nrel, abs, err := model.ResolveDataAssetPath(\"<encBoxID>/assets/x.png\")\n\n// after — use the encryption-aware read path\ndata, err := model.ReadAssetBytesInBox(\"<encBoxID>\", \"assets/x.png\")","handlingStrategy":"validation","validationCode":"parts := strings.Split(filepath.ToSlash(filepath.Clean(assetPath)), \"/\")\nif len(parts) > 2 && ast.IsNodeIDPattern(parts[0]) && model.IsEncryptedBox(parts[0]) {\n    // route to the encryption-aware API instead\n    return model.ReadAssetBytesInBox(parts[0], assetPath)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use ReadAssetBytesInBox for encrypted-notebook assets — it decrypts correctly.","Do not route encrypted-asset access through generic resolvers.","Ensure the encrypted notebook is unlocked in the session."],"tags":["security","assets","encryption","notebook"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}