siyuan-note/siyuan · error · newObsidianUserError
345
345
Error message
read Markdown [%s]: %w
What it means
This error wraps a failure to read an Obsidian vault Markdown file during import. The kernel opens each analyzed .md file to stage its content; if the file cannot be opened or its content changed since analysis (errObsidianSourceChanged maps to language 346), a user-facing localized error is produced with the file's relative path.
Solutions
- Close apps syncing the vault and re-run the import so analysis and staging see a stable snapshot
- Verify the file at the reported relative path still exists and is readable (check permissions, open it in an editor)
- Re-run the import without modifying the vault while it runs; if it persists, copy the vault to a local folder and import from there
Example fix
// before: importing directly from a synced cloud vault
importObsidian("C:/Users/me/OneDrive/Obsidian/MyVault")
// after: pause sync, or import from a stable local copy
// copy MyVault to D:/tmp/MyVault, then:
importObsidian("D:/tmp/MyVault") Defensive patterns
Strategy: validation
Validate before calling
const fs = require('fs');
function assertReadable(absPath) {
const fd = fs.openSync(absPath, 'r');
fs.closeSync(fd);
}
// call for each vault .md file before starting the import
files.forEach(f => assertReadable(f.absPath)); Try / catch
try { await apiImportObsidian(vaultPath); }
catch (e) {
if (/read Markdown \[/.test(e.msg)) {
console.error('Unreadable vault file:', e.msg); // fix permissions/restore file, retry
}
} Prevention
- Pause cloud sync (Syncthing/iCloud/OneDrive) for the vault during import
- Copy the vault to a local stable folder and import the copy
- Check file permissions on the vault before starting
- Close Obsidian and other editors that may lock files
When it happens
Trigger: os.Open fails on file.AbsPath during validateObsidianReadableFile (file deleted, permission denied, path too long), or the file's size/mtime metadata differs from what was recorded during analysis (errObsidianSourceChanged).
Common situations: Vault edited or synced (Syncthing/iCloud/OneDrive) while the import is running; files moved or renamed in the vault between analysis and staging; read-protected files; a locked file on Windows opened by another program.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- read Vault directory
- read Vault path [ ]
- Vault paths differ only by letter case
- : normalize Vault path
- : read Vault config directory
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/a1f0b66ccf97b98e.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/import_obsidian.go:953
if err != nil {
return nil, err
}
after, err := os.Stat(file.AbsPath)
if err != nil {
return nil, err
}
if before.Size() != after.Size() || !before.ModTime().Equal(after.ModTime()) {
return nil, fmt.Errorf("%w while reading", errObsidianSourceChanged)
}
return data, nil
}
func newObsidianReadUserError(file *obsidianSourceFile, err error) error {
language := 345
if errors.Is(err, errObsidianSourceChanged) {
language = 346
}
return newObsidianUserError(language, file.RelPath, fmt.Errorf("read Markdown [%s]: %w", file.RelPath, err))
}
func validateObsidianReadableFile(file *obsidianSourceFile) error {
opened, err := os.Open(file.AbsPath)
if err != nil {
return err
}
defer opened.Close()
buffer := make([]byte, 1)
_, err = opened.Read(buffer)
if err != nil && !errors.Is(err, io.EOF) {
return err
}
return validateObsidianSourceMetadata(file)
}
func buildObsidianHeadingIndex(doc *obsidianDocPlan, tree *parse.Tree) {
stack := make([]string, 6)View on GitHub (pinned to 8641553a1f)