siyuan-note/siyuan · error
resolve asset [ ] failed
Error message
resolve asset [%s] failed: %w
What it means
ResolveDataAssetPath resolves a data-relative asset path and, as its final safety step, re-evaluates the full asset path through symlinks/junctions via ResolveAssetPathWithMissingLeaf. This error wraps any failure of that resolution (e.g. broken symlink, permission error, filesystem error) with the original absolute path for context. It is thrown to prevent proceeding with an unverifiable path.
Solutions
- Check the target of the wrapped path: run filepath.EvalSymlinks on it manually or `ls -l` / `readlink -f` to find the broken link
- Delete or repair the dangling symlink inside <workspace>/data/.../assets/ so a real file exists at the target
- If the asset genuinely should not exist, remove the stale reference from the document and re-run the operation
- Ensure filesystem permissions allow the kernel process to traverse every directory component of the path
Example fix
// before: broken symlink left in assets ln -s /nonexistent/target.png assets/foo.png // after: remove or repair the link rm assets/foo.png # or: ln -s /valid/target.png assets/foo.png
Defensive patterns
Strategy: try-catch
Validate before calling
if _, statErr := os.Stat(absPath); statErr != nil {
// path or symlink target missing; repair before calling ResolveDataAssetPath
}
if real, evalErr := filepath.EvalSymlinks(absPath); evalErr != nil {
// unresolvable symlink; remove or fix it first
} Type guard
func resolvableAsset(p string) bool {
_, err := filepath.EvalSymlinks(p)
return err == nil
} Try / catch
rel, abs, err := model.ResolveDataAssetPath(assetPath)
if err != nil {
var broken bool
if _, evalErr := filepath.EvalSymlinks(abs); evalErr != nil {
broken = true // dangling symlink; clean up then retry
}
return fmt.Errorf("asset %s unusable (broken=%v): %w", assetPath, broken, err)
} Prevention
- Never leave dangling symlinks inside <workspace>/data/**/assets/
- After restoring a workspace from backup, audit symlink targets with `find data -type l -exec file {} \;`
- Check os.Stat/EvalSymlinks on asset paths before registering them in documents
When it happens
Trigger: Calling ResolveDataAssetPath (directly or via ResolveUnusedDataAssetPath, PrepareAgentMessageImage, assetStat, deferredAssetPathFromFiles) with an absolute path whose trailing component cannot be resolved — most commonly a broken symlink pointing to a non-existent target, or a symlink loop/permission failure on any component.
Common situations: An asset file was deleted but a symlink to it remains in the workspace; a synced workspace restored symlinks whose targets are missing; moving a notebook outside the workspace while keeping symlinks inside assets/.
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
- asset path contains an unresolved symbolic link
- asset path resolves outside assets directory
- asset path resolves outside notebook assets directory
- attribute view definition is not a regular file
- encrypted document is a symbolic link
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/77507ef5c7f9aa0e.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/assets.go:1099
resolvedRoot, evalErr := ResolveAssetPathWithMissingLeaf(assetRoot)
if evalErr != nil {
err = fmt.Errorf("resolve assets directory [%s] failed: %w", assetRoot, evalErr)
return
}
if assetDirIndex > 0 {
notebookRoot := filepath.Join(util.DataDir, parts[0])
resolvedDataDir, dataEvalErr := ResolveRealPath(util.DataDir)
resolvedNotebookRoot, notebookEvalErr := ResolveRealPath(notebookRoot)
if dataEvalErr != nil || notebookEvalErr != nil ||
!gulu.File.IsSubPath(resolvedDataDir, resolvedNotebookRoot) ||
!gulu.File.IsSubPath(resolvedNotebookRoot, resolvedRoot) {
err = fmt.Errorf("notebook asset path resolves outside notebook directory: %s", assetPath)
return
}
}
resolvedPath, evalErr := ResolveAssetPathWithMissingLeaf(absPath)
if evalErr != nil {
err = fmt.Errorf("resolve asset [%s] failed: %w", absPath, evalErr)
return
}
if !gulu.File.IsSubPath(resolvedRoot, resolvedPath) {
err = fmt.Errorf("asset path resolves outside assets directory: %s", assetPath)
return
}
relativePath = filepath.ToSlash(dataRelativePath)
return
}
// ResolveUnusedDataAssetPath 解析 data 相对资源路径,并确认目标当前未被引用。
func ResolveUnusedDataAssetPath(assetPath string) (relativePath, absPath string, err error) {
relativePath, absPath, err = ResolveDataAssetPath(assetPath)
if err != nil {
return
}
View on GitHub (pinned to 9f775e8a12)