{"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":"error_code","errorClass":"obsidianUserError","httpStatus":null,"severity":"error","filePath":"kernel/model/import_obsidian.go","lineNumber":1821,"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":1803,"sourceCodeEnd":1839,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/import_obsidian.go#L1803-L1839","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before: edit vault after analysis -> count mismatch -> code 349\n//   StartObsidianVaultAnalysis(vault)  // ok\n//   <user adds 3 new notes in Obsidian>\n//   StartObsidianVaultImport(taskID, nb)  // fails 349\n\n// after: re-analyse after edits stabilise\nStartObsidianVaultAnalysis(vault)  // fresh snapshot\nStartObsidianVaultImport(newTaskID, nb)","handlingStrategy":"validation","validationCode":"// Snapshot the markdown file set; refuse to import if it changed.\nfunc markdownSet(path string) map[string]bool { /* walk, collect .md keys */ }\n// atAnalysis := markdownSet(vault)\n// if !reflect.DeepEqual(atAnalysis, markdownSet(vault)) { re-analyse }","typeGuard":"null","tryCatchPattern":"null","preventionTips":["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."],"tags":["obsidian-import","revalidation","concurrency","vault-drift"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}