{"record":{"id":"0137a38a822d1b02","repo":"siyuan-note/siyuan","slug":"asset-path-contains-an-unresolved-symbolic-link","errorCode":null,"errorMessage":"asset path contains an unresolved symbolic link [%s]","messagePattern":"asset path contains an unresolved symbolic link \\[(.+?)\\]","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/asset_download_read.go","lineNumber":46,"sourceCode":")\n\n// ResolveAssetPathWithMissingLeaf 校验尚未下载的路径，已存在的父目录仍须通过符号链接校验。\nfunc ResolveAssetPathWithMissingLeaf(absPath string) (string, error) {\n\tcurrent := filepath.Clean(absPath)\n\tvar missing []string\n\tfor {\n\t\tresolved, err := ResolveRealPath(current)\n\t\tif err == nil {\n\t\t\tfor i := len(missing) - 1; i >= 0; i-- {\n\t\t\t\tresolved = filepath.Join(resolved, missing[i])\n\t\t\t}\n\t\t\treturn resolved, nil\n\t\t}\n\t\tif !os.IsNotExist(err) {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif info, statErr := os.Lstat(current); statErr == nil && info.Mode()&os.ModeSymlink != 0 {\n\t\t\treturn \"\", fmt.Errorf(\"asset path contains an unresolved symbolic link [%s]\", current)\n\t\t}\n\t\tparent := filepath.Dir(current)\n\t\tif parent == current {\n\t\t\treturn \"\", err\n\t\t}\n\t\tmissing = append(missing, filepath.Base(current))\n\t\tcurrent = parent\n\t}\n}\n\n// deferredAssetPath 只解析同步清单，不访问网络，也不以未下载状态推断文件已删除。\nfunc deferredAssetPath(relativePath, boxID string, includeEncrypted bool) (string, error) {\n\tfiles, err := DeferredSyncAssets()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn deferredAssetPathFromFiles(relativePath, boxID, includeEncrypted, files)\n}","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/asset_download_read.go#L28-L64","documentation":"ResolveAssetPathWithMissingLeaf walks an asset path from the leaf upward to find the deepest existing prefix. When os.Stat fails with an error that is NOT 'not exist', but Lstat reveals the component is a symlink whose target cannot be resolved (dangling or looping symlink), it refuses to silently treat the path as missing-leaf material and returns 'asset path contains an unresolved symbolic link'. This guards the deferred asset-download mechanism against broken symlinks inside the data directory.","triggerScenarios":"Calling getFile, ResolveDataAssetPath, or deferredAssetPath where a component of the asset path (e.g. assets/link -> missing/target) is a symlink pointing to a nonexistent target; also reached via unusedAssetsContainPath during asset cleanup scans.","commonSituations":"User copied a workspace between filesystems and symlinks broke; a sync tool replaced assets with dangling symlinks; a user manually created a symlink in assets/ to a path that no longer exists; a plugin rewrote asset paths as links.","solutions":["Find the offending symlink with `find data -xtype l` and remove or repair it so it points to an existing target","Replace the symlink with a real copy of the target file inside the assets directory","Re-run the asset resolution/download so the deferred pipeline can fetch the missing file into a real path","If the symlink target exists on another mount, mount it before using the workspace"],"exampleFix":"// shell\n// before: data/assets/foo.png -> /mnt/old/foo.png (target gone)\n// after\ncp /mnt/new/foo.png data/assets/foo.png  # or rm data/assets/foo.png and re-insert the asset","handlingStrategy":"fallback","validationCode":"info, err := os.Lstat(assetPath)\nif err == nil && info.Mode()&os.ModeSymlink != 0 {\n    if _, err := os.Stat(assetPath); err != nil {\n        return fmt.Errorf(\"dangling symlink at %s, repair before use\", assetPath)\n    }\n}","typeGuard":null,"tryCatchPattern":"resolved, err := model.ResolveDataAssetPath(relPath)\nif err != nil && strings.Contains(err.Error(), \"unresolved symbolic link\") {\n    repaired, repErr := repairOrRemoveSymlink(relPath) // replace with real file\n    if repErr != nil { return repErr }\n    resolved, err = model.ResolveDataAssetPath(relPath)\n}","preventionTips":["Periodically scan data/ with `find -xtype l` for dangling symlinks","Copy real files into assets/ instead of symlinking across mounts","Avoid filesystem-level copy tools that turn files into broken links on Windows","Re-verify asset resolution after restoring a workspace from an archive"],"tags":["filesystem","symlink","asset-resolution"],"backgroundTag":"file-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"}