siyuan-note/siyuan · error · errObsidianSourceChanged

during analysis

Error message

%w during analysis

What it means

A Vault file changed between the initial scan and the moment it was read for analysis. readStableObsidianFile stats the file before reading and compares size and modification time against the values captured during the scan; a mismatch is wrapped in errObsidianSourceChanged with the context 'during analysis', so callers can detect it with errors.Is. This guarantees the import operates on a stable snapshot.

Solutions

  1. Close Obsidian and any other editors, then re-run the import
  2. Pause cloud-sync/backup tools that touch the vault during import
  3. Make a static copy of the vault and import the copy
  4. Re-run the import — a stable vault will pass the before-stat check

Example fix

// before: importing a live vault
importFrom(vaultDir)
// after: import a quiescent snapshot
cp -r vaultDir /tmp/vault-snapshot
importFrom("/tmp/vault-snapshot")
Defensive patterns

Strategy: retry

Try / catch

try {
  await importObsidianVault(vaultPath);
} catch (e) {
  if (errors.Is(e, errObsidianSourceChanged)) {
    await retryImport(vaultPath); // after quiescing editors/sync
  }
}

Prevention

When it happens

Trigger: os.Stat(file.AbsPath) succeeds but before.Size() != file.Size or !before.ModTime().Equal(file.ModTime) — the file was modified (or replaced) after scanObsidianVaultFiles recorded its metadata.

Common situations: Obsidian (or another editor) is open and autosaves during the import; a sync client (iCloud/Dropbox/Syncthing) updates files concurrently; backup tools touch mtimes.

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/013215a305918af8. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/import_obsidian.go:932

			}
		}
		tree, _, _, _ := parseStdMd(data)
		if tree == nil {
			return newObsidianUserError(347, doc.Source.RelPath,
				fmt.Errorf("parse Markdown [%s] failed", doc.Source.RelPath))
		}
		analyzeObsidianMarkdownLinks(vault, doc, tree)
	}
	return nil
}

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) {

View on GitHub (pinned to 9f775e8a12)