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

  1. Close Obsidian/editors and pause sync tools, then retry the import
  2. Import from a static copy of the vault instead of the live directory
  3. Ensure editors save atomically (most do; Obsidian autosave can be disabled temporarily)
  4. 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

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)