siyuan-note/siyuan · error
export source [ ] is not a regular file
Error message
export source [%s] is not a regular file
What it means
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.
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
Example fix
// before
copyExportFileToDestination(assetDir, destPath) // assetDir is a folder
// after
entries, _ := os.ReadDir(assetDir)
for _, e := range entries {
if info, err := e.Info(); err == nil && info.Mode().IsRegular() {
copyExportFileToDestination(filepath.Join(assetDir, e.Name()), filepath.Join(destPath, e.Name()))
}
} Defensive patterns
Strategy: validation
Validate before calling
const stat = await fs.promises.lstat(src);
if (!stat.isFile()) throw new Error(`${src} is not a regular file; enumerate and copy files individually`); Type guard
const isRegularFile = (s) => s.isFile();
Try / catch
try {
await copyExportFileToDestination(src, dst);
} catch (err) {
if (String(err?.message).includes('is not a regular file')) {
if ((await fs.promises.lstat(src)).isDirectory()) {
return copyDirRecursive(src, dst); // handle directories explicitly
}
throw err;
}
throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Conf.Language(14) (copy assets failed: )
- Conf.Language(14) (copy data dir failed: )
- Conf.Language(14) (copy docx failed: )
- Conf.Language(14) (copy resource failed: )
- Conf.Language(242)
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/3a52332a2834b2fc.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/api/export.go:900
if err := copyExportFileToDestination(srcFullPath, dest); err != nil {
logging.LogErrorf("copy export file [%s] to [%s] failed: %s", srcFullPath, dest, err)
ret.Code = -1
ret.Msg = err.Error()
return contractFailure[apicontract.Null](ret)
}
return apicontract.Success(apicontract.Null{})
})
func copyExportFileToDestination(src, dest string) (err error) {
filelock.Lock(src)
defer filelock.Unlock(src)
srcInfo, err := os.Lstat(src)
if err != nil {
return err
}
if !srcInfo.Mode().IsRegular() {
return fmt.Errorf("export source [%s] is not a regular file", src)
}
srcFile, err := os.Open(src)
if err != nil {
return err
}
defer srcFile.Close()
destDir := filepath.Dir(dest)
if err = os.MkdirAll(destDir, 0755); err != nil {
return err
}
tmpFile, err := os.CreateTemp(destDir, ".siyuan-export-*.tmp")
if err != nil {
return err
}
tmpPath := tmpFile.Name()
tmpClosed := falseView on GitHub (pinned to 9f775e8a12)