{"record":{"id":"fa6f824d6f605de4","repo":"siyuan-note/siyuan","slug":"349","errorCode":"349","errorMessage":"the Markdown file list changed after analysis","messagePattern":"the Markdown file list changed after analysis","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/import_obsidian.go","lineNumber":1843,"sourceCode":"\t\tAnalysis: &ObsidianVaultAnalysis{BlockingErrors: []string{}, Warnings: []string{}},\n\t}\n\tif err := scanObsidianVaultFiles(ctx, fresh, \"\", vault.Root); err != nil {\n\t\treturn err\n\t}\n\toriginalMarkdown := map[string]bool{}\n\tfor _, doc := range vault.Docs {\n\t\tif !doc.Synthetic {\n\t\t\toriginalMarkdown[obsidianPathKey(doc.Source.RelPath)] = true\n\t\t}\n\t}\n\tcurrentMarkdown := map[string]bool{}\n\tfor key, file := range fresh.Files {\n\t\tif file.IsMD {\n\t\t\tcurrentMarkdown[key] = true\n\t\t}\n\t}\n\tif len(originalMarkdown) != len(currentMarkdown) {\n\t\treturn newObsidianUserError(349, \"\", errors.New(\"the Markdown file list changed after analysis\"))\n\t}\n\tfor key := range originalMarkdown {\n\t\tif !currentMarkdown[key] {\n\t\t\treturn newObsidianUserError(349, \"\", errors.New(\"the Markdown file list changed after analysis\"))\n\t\t}\n\t}\n\tif len(vault.Assets) != len(fresh.Assets) {\n\t\treturn newObsidianUserError(349, \"\", errors.New(\"the attachment file list changed after analysis\"))\n\t}\n\tfor key := range vault.Assets {\n\t\tif fresh.Assets[key] == nil {\n\t\t\treturn newObsidianUserError(349, \"\", errors.New(\"the attachment file list changed after analysis\"))\n\t\t}\n\t}\n\tfor _, doc := range vault.Docs {\n\t\tif doc.Synthetic {\n\t\t\tcontinue\n\t\t}","sourceCodeStart":1825,"sourceCodeEnd":1861,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/import_obsidian.go#L1825-L1861","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-run the import from scratch so the analysis and import see the same vault state","Close Obsidian and pause sync clients (iCloud/OneDrive/Syncthing) during the import","Retry once the vault is stable; the error is intentionally transient by design"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// before import, take a snapshot hash/count of *.md files and confirm nothing changes for a few seconds\nn1 := countMD(vaultDir); time.Sleep(2 * time.Second); n2 := countMD(vaultDir)\nif n1 != n2 { /* vault still changing; wait */ }","typeGuard":null,"tryCatchPattern":"if code == 349 {\n    // re-run the import; the error is a protective retry-able abort\n}","preventionTips":["Close Obsidian and pause sync clients for the duration of the import","Do not edit, add, or delete notes between analysis and import confirmation","Import from a static copy of the vault"],"tags":["go","obsidian-import","race-condition"],"backgroundTag":"source-changed-during-operation","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}