siyuan-note/siyuan · error
path is not a child of assets directory
Error message
path is not a child of assets directory: %s
What it means
This is a defense-in-depth check in ResolveDataAssetPath: after locating the `assets` directory segment and joining it to DataDir, the function verifies with gulu.File.IsSubPath that the requested absolute path is actually contained within that assets root. Because earlier cleaning should guarantee containment, hitting this error usually means the computed absPath and the assets root are inconsistent (e.g. edge cases in path separators or the `assets` segment matched at an unexpected index).
Solutions
- Verify the input path is a clean slash-relative path ending in a real file name under `assets/`, not the assets directory itself
- Pass the path through filepath.ToSlash/clean before calling, e.g. `path.Clean(strings.ReplaceAll(p, "\\", "/"))`
- If it occurs on Windows, check path casing/separator normalization in the code that produced the path
- Report as a bug with the exact input path if a plainly valid `assets/...` path triggers it
Example fix
// before
rel, abs, err := model.ResolveDataAssetPath("nb\\assets\\sub\\..\\pic.png")
// after
rel, abs, err := model.ResolveDataAssetPath(path.Clean("nb/assets/sub/../pic.png")) Defensive patterns
Strategy: validation
Validate before calling
p := path.Clean(strings.ReplaceAll(assetPath, "\\", "/"))
if strings.HasSuffix(p, "/assets") {
return errors.New("path must point to a file inside assets/, not the assets dir itself")
} Try / catch
rel, abs, err := model.ResolveDataAssetPath(assetPath)
if err != nil {
if strings.Contains(err.Error(), "not a child of assets directory") {
return fmt.Errorf("malformed asset path %q: %w", assetPath, err)
}
return err
} Prevention
- Pass clean, forward-slash relative paths with a concrete file leaf under assets/
- Never point at the assets directory itself as the target
- On Windows, avoid mixed separators and keep casing consistent with on-disk names
- Treat this error as a caller bug: log the raw input path for diagnosis
When it happens
Trigger: ResolveDataAssetPath called such that absPath (DataDir + cleaned relative path) is not a filesystem child of assetRoot — practically rare since assetRoot is a prefix of absPath; can surface on Windows with unusual path casing/separator forms.
Common situations: Non-normalized paths on Windows (mixed separators, differing case); custom/patched callers that mutate absPath between checks; pathological paths like the leaf being the assets directory itself (`nb/assets`).
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 under an 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/22345e85a394a5fd.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/assets.go:1077
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)
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
}View on GitHub (pinned to 9f775e8a12)