siyuan-note/siyuan · error
resolve assets directory
Error message
resolve assets directory [%s] failed: %w
What it means
After locating the assets root inside a notebook, ResolveDataAssetPath resolves it with ResolveAssetPathWithMissingLeaf (which evaluates symlinks while tolerating a missing final component). If that resolution fails — typically because the assets directory does not exist, is a broken symlink, or the OS rejects the path — the underlying error is wrapped with this message naming the absolute assets root.
Solutions
- Create the missing assets directory (e.g. `<notebookID>/assets/`) referenced by the path
- Check the error wrapped by `%w` to see whether it is not-exist vs permission vs symlink; fix accordingly
- Remove or repair broken symlinks in the notebook directory
- Re-sync the workspace so the assets directory is restored
Example fix
// before
rel, abs, err := model.ResolveDataAssetPath("20240101120000-abc123/assets/pic.png") // assets dir deleted
// after
if _, statErr := os.Stat(filepath.Join(util.DataDir, "20240101120000-abc123", "assets")); os.IsNotExist(statErr) {
os.MkdirAll(filepath.Join(util.DataDir, "20240101120000-abc123", "assets"), 0755)
}
rel, abs, err := model.ResolveDataAssetPath("20240101120000-abc123/assets/pic.png") Defensive patterns
Strategy: validation
Validate before calling
func assetsDirExists(dataDir, notebookID string) bool {
fi, err := os.Stat(filepath.Join(dataDir, notebookID, "assets"))
return err == nil && fi.IsDir()
} Try / catch
rel, abs, err := model.ResolveDataAssetPath(assetPath)
if err != nil {
var pathErr *os.PathError
if strings.Contains(err.Error(), "resolve assets directory") && errors.As(err, &pathErr) && os.IsNotExist(pathErr) {
// recreate the assets dir and retry once
os.MkdirAll(filepath.Join(util.DataDir, notebookID, "assets"), 0755)
rel, abs, err = model.ResolveDataAssetPath(assetPath)
}
if err != nil {
return err
}
} Prevention
- Check that the notebook's assets directory exists before resolving paths into it
- Repair or remove broken symlinks after moving/copying a workspace
- Ensure the sync process materializes the assets directory before references are used
- Watch file/directory permissions on the workspace data dir
When it happens
Trigger: Calling ResolveDataAssetPath for `<notebookID>/assets/<file>` (or global `assets/...`) when the assets directory itself is missing or is an unresolvable/broken symlink, so ResolveAssetPathWithMissingLeaf returns an eval error.
Common situations: Assets folder deleted or renamed on disk while references remain; workspace synced/copied without the assets directory; broken symlinks after moving the workspace; permission problems on the assets directory; sync race where the notebook dir exists but its assets dir was not yet materialized.
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 root [ ] failed
- 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/12b3de77773b2c67.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/assets.go:1083
return
}
}
}
if assetDirIndex < 0 {
err = fmt.Errorf("path is not under an assets directory: %s", assetPath)
return
}
assetRootParts := parts[:assetDirIndex+1]
assetRoot := filepath.Join(util.DataDir, filepath.FromSlash(strings.Join(assetRootParts, "/")))
if !gulu.File.IsSubPath(assetRoot, absPath) {
err = fmt.Errorf("path is not a child of assets directory: %s", assetPath)
return
}
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
}View on GitHub (pinned to 9f775e8a12)