siyuan-note/siyuan · error · obsidianUserError
349
349
Error message
the Markdown file list changed after analysis
What it means
revalidateObsidianVault detects that the set of Markdown files changed between analysis and import: the counts of originalMarkdown and currentMarkdown differ. Reported with code 349 ('The Vault contents changed after analysis; analyze the Vault again'). This is the count-based fast path; the per-key membership check is 789.
Source
Thrown at kernel/model/import_obsidian.go:1821
Analysis: &ObsidianVaultAnalysis{BlockingErrors: []string{}, Warnings: []string{}},
}
if err := scanObsidianVaultFiles(ctx, fresh, "", vault.Root); err != nil {
return err
}
originalMarkdown := map[string]bool{}
for _, doc := range vault.Docs {
if !doc.Synthetic {
originalMarkdown[obsidianPathKey(doc.Source.RelPath)] = true
}
}
currentMarkdown := map[string]bool{}
for key, file := range fresh.Files {
if file.IsMD {
currentMarkdown[key] = true
}
}
if len(originalMarkdown) != len(currentMarkdown) {
return newObsidianUserError(349, "", errors.New("the Markdown file list changed after analysis"))
}
for key := range originalMarkdown {
if !currentMarkdown[key] {
return newObsidianUserError(349, "", errors.New("the Markdown file list changed after analysis"))
}
}
if len(vault.Assets) != len(fresh.Assets) {
return newObsidianUserError(349, "", errors.New("the attachment file list changed after analysis"))
}
for key := range vault.Assets {
if fresh.Assets[key] == nil {
return newObsidianUserError(349, "", errors.New("the attachment file list changed after analysis"))
}
}
for _, doc := range vault.Docs {
if doc.Synthetic {
continue
}View on GitHub (pinned to 251596fc0d)
Solutions
- Stop all external writes to the vault (close Obsidian, pause sync), then re-run StartObsidianVaultAnalysis to produce a fresh task.
- Immediately follow a successful analysis with StartObsidianVaultImport to minimise the window for external mutation.
- If the change is intentional (you added notes), simply re-analyse — the new files will be included.
- For shared/synced vaults, copy to a stable local directory and import from the copy.
Example fix
// before: edit vault after analysis -> count mismatch -> code 349 // StartObsidianVaultAnalysis(vault) // ok // <user adds 3 new notes in Obsidian> // StartObsidianVaultImport(taskID, nb) // fails 349 // after: re-analyse after edits stabilise StartObsidianVaultAnalysis(vault) // fresh snapshot StartObsidianVaultImport(newTaskID, nb)
Defensive patterns
Strategy: validation
Validate before calling
// Snapshot the markdown file set; refuse to import if it changed.
func markdownSet(path string) map[string]bool { /* walk, collect .md keys */ }
// atAnalysis := markdownSet(vault)
// if !reflect.DeepEqual(atAnalysis, markdownSet(vault)) { re-analyse } Type guard
null
Try / catch
null
Prevention
- Do not edit the vault between analysis and import.
- Import immediately after a successful analysis.
- Copy synced vaults to a stable local dir before importing.
When it happens
Trigger: Between the analysis task completing and StartObsidianVaultImport running, markdown files were added or removed in the vault (Obsidian sync pulled new notes, user deleted files, a sync conflict resolved). revalidateObsidianVault re-scans and compares counts; a mismatch aborts import.
Common situations: User continued using Obsidian after analysing; iCloud/Dropbox/Git sync delivered new or deleted .md files; vault on a shared volume edited by another machine; long gap between analysis and import (the Ready task has a TTL after which it expires anyway).
Related errors
- 346
- 345
- attribute view [%s] changed while loading search info
- encrypted notebook is locked, please unlock it first
- OAuth callback was already handled
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/fa6f824d6f605de4.
Report an issue: GitHub.