siyuan-note/siyuan · error
asset must be a regular file
Error message
asset must be a regular file: %s
What it means
resolveRelinkAsset resolves each relink path to a filesystem entry and requires it to be a regular file. If os.Lstat/Stat succeeds but info.Mode().IsRegular() is false (directory, device, fifo, socket, etc.), the resolution fails with this error.
Solutions
- Provide the full path to a single regular file under the notebook's assets directory (e.g. 'assets/foo.png'), not a directory.
- Verify with your filesystem that the target is a plain file (not a directory or special file).
- If you intended to relink multiple assets, issue one mapping per file.
- Run dryRun=true first to catch bad targets before committing the relink.
Example fix
// before
RelinkAsset("assets", "assets-new", false) // directory
// after
RelinkAsset("assets/foo.png", "assets/bar.png", false) Defensive patterns
Strategy: validation
Validate before calling
info, err := os.Stat(resolvedPath)
if err != nil || !info.Mode().IsRegular() {
return fmt.Errorf("target must be a regular file: %s", resolvedPath)
} Type guard
func isRegularFile(p string) bool {
info, err := os.Stat(p)
return err == nil && info.Mode().IsRegular()
} Prevention
- Map one file per relink mapping; never pass directories
- Resolve globs to concrete files before calling the API
- Use dryRun=true to catch bad targets
When it happens
Trigger: Passing a directory path (e.g. 'assets' itself or 'assets/subdir') as OldPath or NewPath in RelinkAsset/AssetRelinkMapping; an asset path that points at a special file; called from scanMetadata during asset resolution.
Common situations: Config mistakes where a folder was configured instead of a single asset; a glob or template expanded to a directory; testing with placeholder paths like 'assets/' that resolve to directories.
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
- asset path must be a file
- can not remove [ ] caused by it is not a dir
- export artifact [ ] is a directory
- font file is empty
- invalid document file
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/f2c28fdb8a8ff06d.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/asset_relink.go:252
}
p.finishPreflight()
return p.scanMetadata(assetRoots)
}
// resolveRelinkAsset 拒绝同名资源歧义,避免把不同目录下的文件视为同一个资源。
func resolveRelinkAsset(roots []string, assetPath string, required bool) (string, error) {
var found string
for _, root := range roots {
candidate := filepath.Join(root, filepath.FromSlash(strings.TrimPrefix(assetPath, "assets/")))
info, err := os.Stat(candidate)
if os.IsNotExist(err) {
continue
}
if err != nil {
return "", err
}
if !info.Mode().IsRegular() {
return "", fmt.Errorf("asset must be a regular file: %s", assetPath)
}
real, err := filepath.EvalSymlinks(candidate)
if err != nil {
return "", err
}
realRoot, err := filepath.EvalSymlinks(root)
if err != nil || !gulu.File.IsSubPath(realRoot, real) {
return "", fmt.Errorf("asset escapes its directory: %s", assetPath)
}
if err = validateRelinkStoragePath(real); err != nil {
return "", err
}
if IsEncryptedAssetPath(real) {
return "", errors.New("encrypted assets are not supported")
}
if found != "" && found != candidate {
return "", fmt.Errorf("ambiguous asset path: %s", assetPath)
}View on GitHub (pinned to 9f775e8a12)