siyuan-note/siyuan · error
path is not under an assets directory
Error message
path is not under an assets directory: %s
What it means
ResolveDataAssetPath only accepts asset paths that live under a directory literally named `assets` — either the global `assets/` at the top of the data dir, or `<notebookID>/assets/...` inside a notebook. After sanitizing the relative path, it searches the path segments for an `assets` segment in a valid position; if none is found (assetDirIndex stays -1), this error is thrown.
Solutions
- Place or move the file under the workspace-level `assets/` directory and reference it as `assets/<file>`
- Place it under the notebook's own assets directory: `<notebookID>/assets/<file>`
- Fix the typo in the path so an `assets` segment appears between the notebook ID (or nothing) and the file name
- If you are resolving non-asset files, use a different resolver — ResolveDataAssetPath is only for assets
Example fix
// before
rel, abs, err := model.ResolveDataAssetPath("20240101120000-abc123/pic.png")
// after
rel, abs, err := model.ResolveDataAssetPath("20240101120000-abc123/assets/pic.png") Defensive patterns
Strategy: validation
Validate before calling
func isAssetPath(p string) bool {
p = path.Clean(strings.ReplaceAll(p, "\\", "/"))
parts := strings.Split(p, "/")
if len(parts) > 1 && parts[0] == "assets" {
return true
}
for i := 1; i < len(parts)-1; i++ {
if parts[i] == "assets" {
return true
}
}
return false
} Try / catch
rel, abs, err := model.ResolveDataAssetPath(assetPath)
if err != nil {
if strings.Contains(err.Error(), "not under an assets directory") {
return fmt.Errorf("%q must be stored under assets/: %w", assetPath, err)
}
return err
} Prevention
- Always store programmatic assets under `assets/` or `<notebookID>/assets/`
- Normalize with path.Clean and forward slashes before passing asset paths
- When generating document paths, insert the assets directory rather than writing files next to .sy files
- Use the asset-upload API instead of hand-building asset file paths
When it happens
Trigger: Calling ResolveDataAssetPath with a path that contains no `assets` directory segment in a valid position, e.g. `20240101120000-abc123/foo.png` (file directly in notebook dir), `somefile.png`, or a path where `assets` appears only as the last segment (`nb/assets` with no leaf) or nested after a non-ID first segment.
Common situations: Hand-built asset paths missing the `assets/` directory; referencing files stored outside any assets folder (e.g. custom data files in the notebook root); typos like `asset/` instead of `assets/`; passing document paths instead of asset paths.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- path is not a child of assets directory
- accessing assets in encrypted notebook
- Conf.Language(0)
- Conf.Language(77)
- read image failed
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/7e4b3a0fbafc5fda.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/assets.go:1070
if parts[i] == "assets" {
assetDirIndex = i
break
}
}
if assetDirIndex > 0 {
boxConfPath := filepath.Join(util.DataDir, parts[0], ".siyuan", "conf.json")
if !filelock.IsExist(boxConfPath) {
err = fmt.Errorf("asset path does not belong to a notebook: %s", assetPath)
return
}
if IsEncryptedBox(parts[0]) {
err = fmt.Errorf("accessing assets in encrypted notebook [%s] is not supported", parts[0])
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)View on GitHub (pinned to 9f775e8a12)