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

  1. Ensure <workspace>/data/assets exists and is accessible; create it if missing
  2. Check filesystem permissions on data/assets so the process can stat and resolve it
  3. Recreate the data/assets directory (EvalSymlinks fails on non-existent paths) and re-import the asset
  4. 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

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


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)