siyuan-note/siyuan · warning

349

349

Error message

the Markdown file list changed after analysis

What it means

Before starting an Obsidian import, the vault is re-scanned (fresh analysis) and its Markdown file set is compared against the set captured at the original analysis. If the counts differ, the importer aborts with localized user error 349: the source vault changed between analysis and import, so proceeding could import inconsistent data.

Solutions

  1. Re-run the import from scratch so the analysis and import see the same vault state
  2. Close Obsidian and pause sync clients (iCloud/OneDrive/Syncthing) during the import
  3. Retry once the vault is stable; the error is intentionally transient by design
Defensive patterns

Strategy: retry

Validate before calling

// before import, take a snapshot hash/count of *.md files and confirm nothing changes for a few seconds
n1 := countMD(vaultDir); time.Sleep(2 * time.Second); n2 := countMD(vaultDir)
if n1 != n2 { /* vault still changing; wait */ }

Try / catch

if code == 349 {
    // re-run the import; the error is a protective retry-able abort
}

Prevention

When it happens

Trigger: len(originalMarkdown) != len(currentMarkdown) in the pre-import consistency check — a .md file was added to or removed from the vault after the initial analysis pass.

Common situations: Obsidian (or a sync client) was open and syncing while the user clicked import; a background backup tool added files; user edited the vault between the analysis dialog and confirming import.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/fa6f824d6f605de4. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/import_obsidian.go:1843

		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 9f775e8a12)