{"record":{"id":"3a52332a2834b2fc","repo":"siyuan-note/siyuan","slug":"export-source-s-is-not-a-regular-file","errorCode":null,"errorMessage":"export source [%s] is not a regular file","messagePattern":"export source \\[(.+?)\\] is not a regular file","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/api/export.go","lineNumber":900,"sourceCode":"\tif err := copyExportFileToDestination(srcFullPath, dest); err != nil {\n\t\tlogging.LogErrorf(\"copy export file [%s] to [%s] failed: %s\", srcFullPath, dest, err)\n\t\tret.Code = -1\n\t\tret.Msg = err.Error()\n\t\treturn contractFailure[apicontract.Null](ret)\n\t}\n\treturn apicontract.Success(apicontract.Null{})\n})\n\nfunc copyExportFileToDestination(src, dest string) (err error) {\n\tfilelock.Lock(src)\n\tdefer filelock.Unlock(src)\n\n\tsrcInfo, err := os.Lstat(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !srcInfo.Mode().IsRegular() {\n\t\treturn fmt.Errorf(\"export source [%s] is not a regular file\", src)\n\t}\n\n\tsrcFile, err := os.Open(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer srcFile.Close()\n\n\tdestDir := filepath.Dir(dest)\n\tif err = os.MkdirAll(destDir, 0755); err != nil {\n\t\treturn err\n\t}\n\ttmpFile, err := os.CreateTemp(destDir, \".siyuan-export-*.tmp\")\n\tif err != nil {\n\t\treturn err\n\t}\n\ttmpPath := tmpFile.Name()\n\ttmpClosed := false","sourceCodeStart":882,"sourceCodeEnd":918,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/api/export.go#L882-L918","documentation":"copyExportFileToDestination copies a single file to an export destination. It calls os.Lstat on the source and requires a regular file; anything else (directory, symlink target that is not regular per mode check, device, missing special file) is rejected with \"export source is not a regular file\" so the copy never attempts to read a non-file as one.","triggerScenarios":"Export flows (or direct calls to copyExportFileToDestination) where src points to a directory instead of a file, a dangling or special filesystem entry, or a path that was expected to be a file but is a folder after reorganizing assets/templates.","commonSituations":"Configured export template/asset path accidentally pointing at a directory; a plugin passing a folder path where a file path is required; assets replaced by directories after a sync; platform-specific entries (sockets/fifos) inside an export folder.","solutions":["Pass the path of a regular file; if the source is a directory, enumerate it and call the copy per file (or use the recursive copy helper if available)","Check the entry first: os.Stat / fs.existsSync + isFile() before invoking the copy","Fix the configuration or plugin code that supplies the wrong path (e.g. a template directory instead of the template file)","Remove or replace non-regular entries (dangling symlinks, sockets) inside the exported asset directory"],"exampleFix":"// before\ncopyExportFileToDestination(assetDir, destPath) // assetDir is a folder\n// after\nentries, _ := os.ReadDir(assetDir)\nfor _, e := range entries {\n  if info, err := e.Info(); err == nil && info.Mode().IsRegular() {\n    copyExportFileToDestination(filepath.Join(assetDir, e.Name()), filepath.Join(destPath, e.Name()))\n  }\n}","handlingStrategy":"validation","validationCode":"const stat = await fs.promises.lstat(src);\nif (!stat.isFile()) throw new Error(`${src} is not a regular file; enumerate and copy files individually`);","typeGuard":"const isRegularFile = (s) => s.isFile();","tryCatchPattern":"try {\n  await copyExportFileToDestination(src, dst);\n} catch (err) {\n  if (String(err?.message).includes('is not a regular file')) {\n    if ((await fs.promises.lstat(src)).isDirectory()) {\n      return copyDirRecursive(src, dst); // handle directories explicitly\n    }\n    throw err;\n  }\n  throw err;\n}","preventionTips":["Verify paths with os.Stat/IsRegular before export operations","Configure export templates/assets with file paths, not directories","Prune dangling symlinks and special files from exported asset folders","Wrap copy helpers so directory inputs recurse instead of erroring"],"tags":["export","filesystem","siyuan"],"backgroundTag":"path-is-not-a-directory","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"}