siyuan-note/siyuan · error · errObsidianSourceChanged
while reading
Error message
%w while reading
What it means
A Vault file changed while it was being read: the post-read os.Stat shows a size or modification time different from the pre-read stat, so the partially-read data is discarded and errObsidianSourceChanged is wrapped with the context 'while reading'. This prevents importing a torn mix of old and new content.
Solutions
- Close Obsidian/editors and pause sync tools, then retry the import
- Import from a static copy of the vault instead of the live directory
- Ensure editors save atomically (most do; Obsidian autosave can be disabled temporarily)
- Simply re-run the import once the vault is quiet
Defensive patterns
Strategy: retry
Try / catch
err := importObsidianVault(vaultPath)
if errors.Is(err, errObsidianSourceChanged) {
err = importObsidianVault(vaultPath) // retry on quiescent vault
} Prevention
- Ensure no writer touches the vault during the import window
- Use editors that save atomically (write-temp-then-rename)
- Import from a static snapshot when the live vault is busy
When it happens
Trigger: After os.ReadFile succeeds, after.Size() != before.Size() or the ModTime differs — the writer saved the file between the pre-read stat and the post-read stat.
Common situations: Obsidian autosave firing mid-import; sync clients replacing files during the scan; editors that write files non-atomically while SiYuan reads them.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/46a8863d463e575c.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/import_obsidian.go:943
func readStableObsidianFile(file *obsidianSourceFile) ([]byte, error) {
before, err := os.Stat(file.AbsPath)
if err != nil {
return nil, err
}
if before.Size() != file.Size || !before.ModTime().Equal(file.ModTime) {
return nil, fmt.Errorf("%w during analysis", errObsidianSourceChanged)
}
data, err := os.ReadFile(file.AbsPath)
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()View on GitHub (pinned to 9f775e8a12)