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

  1. Provide the full path to a single regular file under the notebook's assets directory (e.g. 'assets/foo.png'), not a directory.
  2. Verify with your filesystem that the target is a plain file (not a directory or special file).
  3. If you intended to relink multiple assets, issue one mapping per file.
  4. 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

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


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)