siyuan-note/siyuan · warning
Obsidian source file changed
Error message
Obsidian source file changed
What it means
`errObsidianSourceChanged` signals TOCTOU protection: before analysis or after reading a Vault note, the file's size and mtime are re-stat'ed and compared to the pre-read snapshot; any difference aborts the read of that file so the importer never parses a half-written or concurrently modified file. It is intentionally opaque to users (see `TestObsidianUserErrorDataDoesNotExposeCause`).
Solutions
- Close Obsidian and pause sync clients, then retry the import
- Re-run the import — the check is per-attempt and succeeds when the file is stable
- Copy the Vault to a static local folder and import from the copy
Example fix
// before // import while Obsidian autosaves the note -> stat mismatch obsidianReadFile(file) // wrapped: errObsidianSourceChanged during analysis // after // close Obsidian / pause sync, or import from a static copy obsidianReadFile(file) // stable stat, read succeeds
Defensive patterns
Strategy: retry
Validate before calling
null
Type guard
null
Try / catch
for (let attempt = 0; attempt < 3; attempt++) {
try {
await api.importObsidianVault(vaultPath);
break;
} catch (e) {
if (!isSourceChangedError(e)) throw e;
await sleep(1000); // let writer settle, then retry
}
} Prevention
- Close Obsidian and pause sync clients during import
- Import from a static local copy of the vault
- Avoid editing or re-timestamping vault notes while an import is running
When it happens
Trigger: `obsidianReadStable` path: `before.Size() != file.Size || !before.ModTime().Equal(file.ModTime)` after analysis, or the after-read stat differs — i.e. the `.md` file changed size or mtime between stat, analysis, and `os.ReadFile`.
Common situations: Obsidian (or a sync client like Obsidian Sync/iCloud/Dropbox) is open and autosaving while the import runs; an editor/sorter touched the note mid-import; filesystem timestamps were rewritten by a sync scan.
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/08aa1ae8416434e0.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/import_obsidian.go:235
func (err *obsidianUserError) Unwrap() error {
return err.Cause
}
func newObsidianUserError(detailLanguage int, relPath string, cause error) error {
return &obsidianUserError{DetailLanguage: detailLanguage, RelPath: relPath, Cause: cause}
}
var (
obsidianTasksMu sync.Mutex
obsidianTasks = map[string]*obsidianTask{}
obsidianActive string
errObsidianVaultUnreadable = errors.New("Obsidian Vault is unreadable")
errObsidianVaultNotDirectory = errors.New("Obsidian Vault path is not a directory")
errObsidianVaultUnsafePath = errors.New("Obsidian Vault path is unsafe")
errObsidianVaultConfigMissing = errors.New("Obsidian Vault config directory is missing")
errObsidianVaultMarkdownMissing = errors.New("Obsidian Vault has no readable Markdown")
errObsidianSourceChanged = errors.New("Obsidian source file changed")
obsidianBlockIDPattern = regexp.MustCompile(`(?m)(?:^|[ \t])\^([A-Za-z0-9-]+)[ \t]*$`)
obsidianQuotePattern = regexp.MustCompile(`^((?:[ \t]*>[ \t]?)+)(.*)$`)
obsidianListItemPattern = regexp.MustCompile(`^([ \t]*(?:[-+*]|\d+[.)])[ \t]+)(.*)$`)
obsidianFootnotePattern = regexp.MustCompile(`(?m)\[\^[^\]\r\n]+\]`)
)
func StartObsidianVaultAnalysis(localPath string) (*ObsidianVaultTask, error) {
var replacedTaskID string
obsidianTasksMu.Lock()
if obsidianActive != "" {
if active := obsidianTasks[obsidianActive]; active != nil && !isObsidianTerminalState(active.State) {
if !isObsidianPreImportState(active.State) {
obsidianTasksMu.Unlock()
return nil, errors.New(Conf.Language(329))
}
if active.Cancel != nil {
active.Cancel()View on GitHub (pinned to 9f775e8a12)