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

  1. Verify the assets root exists and is traversable: `ls -la <DataDir>/assets` and check each parent.
  2. Recreate the directory if missing (SiYuan recreates it on demand for new uploads).
  3. Fix or remove broken symlinks in the path chain.
  4. 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

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


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/de907c0d8146b5dc. Report an issue: GitHub.