siyuan-note/siyuan · error
resolve assets root [ ] failed
Error message
resolve assets root [%s] failed: %w
What it means
When the found asset path is a symlink, getAssetAbsPath resolves both the symlink target and the data/assets root with filepath.EvalSymlinks. This error is returned if EvalSymlinks fails on the assets root itself (util.GetDataAssetsAbsPath()), wrapped as "resolve assets root [%s] failed: %w". It indicates the workspace's data/assets directory cannot be canonically resolved on disk.
Solutions
- Ensure <workspace>/data/assets exists and is accessible; create it if missing
- Check filesystem permissions on data/assets so the process can stat and resolve it
- Recreate the data/assets directory (EvalSymlinks fails on non-existent paths) and re-import the asset
- If data/assets is itself a symlink, verify the target exists and is reachable by the kernel process
Example fix
// before
assetsRoot := util.GetDataAssetsAbsPath()
// after
assetsRoot := util.GetDataAssetsAbsPath()
if _, err := os.Stat(assetsRoot); os.IsNotExist(err) {
os.MkdirAll(assetsRoot, 0755) // EvalSymlinks fails when the root is missing
} Defensive patterns
Strategy: fallback
Validate before calling
if _, err := os.Stat(util.GetDataAssetsAbsPath()); err != nil {
os.MkdirAll(util.GetDataAssetsAbsPath(), 0755)
} Try / catch
abs, err := model.GetAssetAbsPath(rel)
if err != nil && strings.HasPrefix(err.Error(), "resolve assets root") {
log.Printf("data/assets broken, check workspace layout: %v", err)
} Prevention
- Keep data/assets present and writable in every workspace
- Verify symlink targets survive workspace moves and restores
- Test workspaces on network mounts for EvalSymlinks behavior
When it happens
Trigger: getAssetAbsPath hits the symlink branch (EvalSymlinks(p) succeeds and differs from p) but filepath.EvalSymlinks(assetsRoot) returns an error — e.g. the data/assets directory does not exist, was deleted, or a permission/IO error prevents resolution.
Common situations: Workspaces where data/assets was removed or renamed while a doc still references a symlinked asset; symlink setups on network mounts or Windows junctions where EvalSymlinks fails; partial workspace restores missing the assets folder.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- resolve assets directory
- accessing assets in encrypted notebook
- Conf.Language(0)
- path is not a child of assets directory
- path is not under an assets directory
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/de907c0d8146b5dc.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/assets.go:1300
return "", err
}
return "", fmt.Errorf(Conf.Language(12), relativePath)
}
func getAssetAbsPath(relativePath string, includeEncrypted bool) (absPath string, err error) {
relativePath = filepath.ToSlash(relativePath)
// 在 data 文件夹下搜索,主要是 data/assets 文件夹
p := filepath.Join(util.DataDir, relativePath)
if gulu.File.IsExist(p) {
if !gulu.File.IsSubPath(util.WorkspaceDir, p) {
return "", fmt.Errorf("[%s] is not sub path of workspace", p)
}
// 解析符号链接,验证真实路径仍在 data/assets/ 下
if realP, evalErr := filepath.EvalSymlinks(p); evalErr == nil && realP != p {
assetsRoot := util.GetDataAssetsAbsPath()
realAssetsRoot, rootEvalErr := filepath.EvalSymlinks(assetsRoot)
if rootEvalErr != nil {
return "", fmt.Errorf("resolve assets root [%s] failed: %w", assetsRoot, rootEvalErr)
}
if !gulu.File.IsSubPath(realAssetsRoot, realP) {
return "", fmt.Errorf("symlink [%s] resolves outside data/assets: [%s]", p, realP)
}
// 安全校验使用解析后的路径,返回原路径以便下游与 DataDir 保持同一路径形式
return p, nil
}
return p, nil
}
// 在文档同级 assets 文件夹下搜索
if !strings.HasPrefix(relativePath, "assets/") {
return "", nil
}
notebooks, err := ListNotebooks()
if err != nil {
return "", errors.New(Conf.Language(0))
}View on GitHub (pinned to 9f775e8a12)