{"record":{"id":"f58e43878a27af63","repo":"siyuan-note/siyuan","slug":"conf-language-12-localized-asset-not-found-messa","errorCode":null,"errorMessage":"Conf.Language(12) (localized asset-not-found message)","messagePattern":"Conf\\.Language\\(12\\) \\(localized asset-not-found message\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/assets.go","lineNumber":1261,"sourceCode":"\t\t\t// 验证解析后的路径仍在 <boxID>/assets/ 或全局 data/assets/ 下\n\t\t\texpectedPrefix := filepath.Join(util.DataDir, \"assets\")\n\t\t\tif boxID != \"\" {\n\t\t\t\texpectedPrefix = filepath.Join(util.DataDir, boxID, \"assets\")\n\t\t\t}\n\t\t\tif !gulu.File.IsSubPath(expectedPrefix, realP) {\n\t\t\t\treturn \"\", fmt.Errorf(\"symlink [%s] resolves outside assets directory: [%s]\", p, realP)\n\t\t\t}\n\t\t}\n\t\treturn p, nil\n\t}\n\t// 非加密 box 的资源可能回退到全局 data/assets（兼容旧笔记本结构）\n\tif deferredPath, deferredErr := deferredAssetPath(relativePath, boxID, true); deferredErr != nil || deferredPath != \"\" {\n\t\treturn deferredPath, deferredErr\n\t}\n\tif !IsEncryptedBox(boxID) {\n\t\treturn GetAssetAbsPathWithOpt(relativePath, false)\n\t}\n\treturn \"\", fmt.Errorf(Conf.Language(12), relativePath)\n}\n\n// GetAssetAbsPathWithOpt 与 GetAssetAbsPath 一致，但可通过 includeEncrypted 控制是否遍历加密 box。\n// serveAssets 传 true（下游 serveEncryptedAsset 会按锁定状态 fail-closed），其他调用方传 false（安全跳过）。\nfunc GetAssetAbsPathWithOpt(relativePath string, includeEncrypted bool) (string, error) {\n\trelativePath = strings.TrimSpace(relativePath)\n\tif idx := strings.Index(relativePath, \"?\"); idx >= 0 {\n\t\trelativePath = relativePath[:idx]\n\t}\n\n\tabsPath, err := getAssetAbsPath(relativePath, includeEncrypted)\n\tif err == nil && absPath != \"\" {\n\t\treturn absPath, nil\n\t}\n\n\tif err != nil {\n\t\treturn \"\", err\n\t}","sourceCodeStart":1243,"sourceCodeEnd":1279,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/assets.go#L1243-L1279","documentation":"This is the terminal failure of GetAssetAbsPathInBox: after the box-local lookup, the deferred global data/assets fallback, and the non-encrypted-box fallback all fail to find the file, it returns the localized 'asset not found' message (Conf.Language(12)) interpolated with the relative path. It means the path was valid but no such asset exists in any searchable location.","triggerScenarios":"Requesting an asset path that does not exist under <box>/assets/, data/assets/, or any non-encrypted box; requesting assets of encrypted notebooks through the non-privileged path; a typo'd or renamed asset filename; assets lost by sync or deletion.","commonSituations":"Document references an asset that was deleted or renamed; sync conflicts dropped the file; caller asks for an asset belonging to an encrypted box without the unlocked context; mobile/older clients referencing assets with stale paths.","solutions":["Verify the file exists on disk under <box>/assets/ or data/assets/ with the exact filename","Re-upload/copy the missing asset and update the document reference to the new path","If the asset is in an encrypted box, ensure the box is unlocked so the lookup path can access it","Check sync history or file history (data/history) to recover the deleted asset"],"exampleFix":"// before\nGetAssetAbsPathInBox(\"assets/old-name.png\", boxID) // deleted\n// after\nGetAssetAbsPathInBox(\"assets/renamed-copy.png\", boxID) // existing asset","handlingStrategy":"try-catch","validationCode":"// check existence before resolving\nconst { data } = await fetchPost(\"/api/filetree/getDoc\", { id: blockID });\n// or: verify the file exists via /api/file/getFile path listing","typeGuard":null,"tryCatchPattern":"p, err := GetAssetAbsPathInBox(rel, boxID)\nif err != nil {\n  // localized not-found: notify user, offer history recovery\n  util.PushMsg(fmt.Sprintf(\"asset missing: %s\", rel), 3000)\n  return\n}","preventionTips":["Confirm the asset filename matches exactly what is on disk before lookup","Use SiYuan's asset upload flow so paths are canonical from the start","Recover deleted assets from data/history instead of reusing stale references","For encrypted boxes, ensure the box is unlocked before resolving assets"],"tags":["asset-not-found","file-not-found","i18n"],"backgroundTag":"resource-not-found","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}