{"record":{"id":"f2c28fdb8a8ff06d","repo":"siyuan-note/siyuan","slug":"asset-must-be-a-regular-file-s","errorCode":null,"errorMessage":"asset must be a regular file: %s","messagePattern":"asset must be a regular file: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/asset_relink.go","lineNumber":252,"sourceCode":"\t}\n\tp.finishPreflight()\n\treturn p.scanMetadata(assetRoots)\n}\n\n// resolveRelinkAsset 拒绝同名资源歧义，避免把不同目录下的文件视为同一个资源。\nfunc resolveRelinkAsset(roots []string, assetPath string, required bool) (string, error) {\n\tvar found string\n\tfor _, root := range roots {\n\t\tcandidate := filepath.Join(root, filepath.FromSlash(strings.TrimPrefix(assetPath, \"assets/\")))\n\t\tinfo, err := os.Stat(candidate)\n\t\tif os.IsNotExist(err) {\n\t\t\tcontinue\n\t\t}\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif !info.Mode().IsRegular() {\n\t\t\treturn \"\", fmt.Errorf(\"asset must be a regular file: %s\", assetPath)\n\t\t}\n\t\treal, err := filepath.EvalSymlinks(candidate)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\trealRoot, err := filepath.EvalSymlinks(root)\n\t\tif err != nil || !gulu.File.IsSubPath(realRoot, real) {\n\t\t\treturn \"\", fmt.Errorf(\"asset escapes its directory: %s\", assetPath)\n\t\t}\n\t\tif err = validateRelinkStoragePath(real); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif IsEncryptedAssetPath(real) {\n\t\t\treturn \"\", errors.New(\"encrypted assets are not supported\")\n\t\t}\n\t\tif found != \"\" && found != candidate {\n\t\t\treturn \"\", fmt.Errorf(\"ambiguous asset path: %s\", assetPath)\n\t\t}","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/asset_relink.go#L234-L270","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nRelinkAsset(\"assets\", \"assets-new\", false) // directory\n// after\nRelinkAsset(\"assets/foo.png\", \"assets/bar.png\", false)","handlingStrategy":"validation","validationCode":"info, err := os.Stat(resolvedPath)\nif err != nil || !info.Mode().IsRegular() {\n    return fmt.Errorf(\"target must be a regular file: %s\", resolvedPath)\n}","typeGuard":"func isRegularFile(p string) bool {\n    info, err := os.Stat(p)\n    return err == nil && info.Mode().IsRegular()\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["filesystem","validation","asset-relink"],"backgroundTag":"invalid-argument-value","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}