siyuan-note/siyuan · error
resolve assets root [%s] failed: %w
Error message
resolve assets root [%s] failed: %w
What it means
Thrown by getAssetAbsPath (kernel/model/assets.go:1154) when filepath.EvalSymlinks fails on util.GetDataAssetsAbsPath() — the global `data/assets` root. EvalSymlinks fails when the assets root directory does not exist, contains a broken link in its path, or is not traversable. It blocks the symlink-escape check from running on an unresolvable root.
Source
Thrown at kernel/model/assets.go:1154
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 251596fc0d)
Solutions
- Verify the assets root exists and is traversable: `ls -la <DataDir>/assets` and check each parent.
- Recreate the directory if missing (SiYuan recreates it on demand for new uploads).
- Fix or remove broken symlinks in the path chain.
- Correct ownership/permissions so the kernel process can traverse to the assets root.
Defensive patterns
Strategy: validation
Validate before calling
// Verify the global assets root is resolvable before resolving a symlinked global asset.
root := util.GetDataAssetsAbsPath()
if _, err := filepath.EvalSymlinks(root); err != nil {
return fmt.Errorf("global assets root unavailable: %w", err)
} Try / catch
if _, err := model.GetAssetAbsPath(ref); err != nil && strings.Contains(err.Error(), "resolve assets root") {
// global data/assets missing/blocked; recreate or fix perms, then retry
} Prevention
- Keep data/assets on reliable local storage.
- Fix ownership so the kernel can traverse to the assets root.
- Recreate data/assets if it is deleted (SiYuan recreates it for new uploads).
When it happens
Trigger: Calling the global asset resolver for a global asset that is itself a symlink, at a moment when the `data/assets` directory (or one of its parents) cannot be resolved: missing directory, dangling symlink in the chain, or insufficient permissions.
Common situations: The global `data/assets` directory was deleted or never created; a parent of it is a broken symlink after a workspace move; permission change preventing traversal; partial restore.
Related errors
- resolve assets directory [%s] failed: %w
- resolve asset [%s] failed: %w
- asset path resolves outside assets directory: %s
- [%s] is not sub path of workspace
- symlink [%s] resolves outside workspace: [%s]
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/de907c0d8146b5dc.
Report an issue: GitHub.