siyuan-note/siyuan · error
asset path [%s] is outside assets directory [%s]
Error message
asset path [%s] is outside assets directory [%s]
What it means
assetPathMapKey computes an asset's relative path inside an assets directory and rejects any path that is not strictly contained in it: equal to '.', '..', starting with '../', or absolute. This is a path-traversal guard ensuring every asset key maps under 'assets/'. filepath.Rel produced a relative path that escapes the assets directory, so the entry is refused instead of generating a key outside the assets tree.
Source
Thrown at kernel/model/assets.go:2827
return nil
})
if walkErr != nil {
if os.IsNotExist(walkErr) {
return
}
return nil, fmt.Errorf("walk global assets [%s] failed: %w", dataAssetsAbsPath, walkErr)
}
return
}
func assetPathMapKey(assetsDirPath, assetPath string, isDir bool) (ret string, err error) {
relPath, err := filepath.Rel(assetsDirPath, assetPath)
if err != nil {
return
}
relPath = filepath.ToSlash(relPath)
if relPath == "." || relPath == ".." || strings.HasPrefix(relPath, "../") || path.IsAbs(relPath) {
err = fmt.Errorf("asset path [%s] is outside assets directory [%s]", assetPath, assetsDirPath)
return
}
ret = path.Join("assets", relPath)
if isDir {
ret += "/"
}
return
}
// copyBoxAssetsToDataAssets 将笔记本路径下所有(包括子文档)的 assets 复制一份到 data/assets 中。
func copyBoxAssetsToDataAssets(boxID string) error {
boxLocalPath := filepath.Join(util.DataDir, boxID)
return copyAssetsToDataAssets(boxLocalPath)
}
// copyDocAssetsToDataAssets 将文档路径下所有(包括子文档)的 assets 复制一份到 data/assets 中。
func copyDocAssetsToDataAssets(boxID, parentDocPath string) error {View on GitHub (pinned to 8641553a1f)
Solutions
- Locate the offending path reported in the message and check whether it is a symlink escaping the assets directory; remove or relocate the symlink.
- Ensure only files physically under the assets directory are referenced; copy external files into data/assets instead of linking them.
- If you call this internal pipeline yourself, pass paths that are descendants of assetsDirPath (verify with filepath.Rel == no '../' prefix).
- Report it as an internal invariant violation if the path came from the kernel's own walk, since walked entries should always be inside the dir.
Example fix
// before: symlink inside assets escaping the directory ln -s /home/user/other-files data/assets/linked // after: copy the content instead of linking cp -r /home/user/other-files data/assets/other-files
Defensive patterns
Strategy: validation
Validate before calling
function isInsideAssets(assetPath, assetsDir) {
const rel = path.relative(assetsDir, assetPath);
return rel !== "" && rel !== ".." && !rel.startsWith(".." + path.sep) && !path.isAbsolute(rel);
} Prevention
- Never symlink external files into data/assets; copy them instead.
- Validate any externally supplied asset path is a descendant of the assets directory before use.
- Keep workspace assets on one filesystem to avoid odd relative-path resolutions.
- Audit data/assets for symlinks when importing notebooks from other machines.
When it happens
Trigger: An asset path passed to assetPathMapKey (during asset map building for cleanup/refresh) resolves to a location outside the assets dir, e.g. a symlink under data/assets pointing elsewhere, or an absolute path supplied that is not a descendant of assetsDirPath.
Common situations: A user placed a symlink inside data/assets pointing to files elsewhere on disk; storage layouts where assets dirs are linked; custom tooling that feeds absolute asset paths into the asset-map pipeline.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- asset path is outside data directory
- asset path escapes data directory: %s
- asset path does not belong to a notebook: %s
- resolve assets directory [%s] failed: %w
- Conf.Language(0) (localized open notebook failure message)
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/3cf6837602f87bc2.
Report an issue: GitHub.