{"record":{"id":"46a8863d463e575c","repo":"siyuan-note/siyuan","slug":"w-while-reading","errorCode":null,"errorMessage":"%w while reading","messagePattern":"%w while reading","errorType":"exception","errorClass":"errObsidianSourceChanged","httpStatus":null,"severity":"error","filePath":"kernel/model/import_obsidian.go","lineNumber":943,"sourceCode":"\nfunc readStableObsidianFile(file *obsidianSourceFile) ([]byte, error) {\n\tbefore, err := os.Stat(file.AbsPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif before.Size() != file.Size || !before.ModTime().Equal(file.ModTime) {\n\t\treturn nil, fmt.Errorf(\"%w during analysis\", errObsidianSourceChanged)\n\t}\n\tdata, err := os.ReadFile(file.AbsPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tafter, err := os.Stat(file.AbsPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif before.Size() != after.Size() || !before.ModTime().Equal(after.ModTime()) {\n\t\treturn nil, fmt.Errorf(\"%w while reading\", errObsidianSourceChanged)\n\t}\n\treturn data, nil\n}\n\nfunc newObsidianReadUserError(file *obsidianSourceFile, err error) error {\n\tlanguage := 345\n\tif errors.Is(err, errObsidianSourceChanged) {\n\t\tlanguage = 346\n\t}\n\treturn newObsidianUserError(language, file.RelPath, fmt.Errorf(\"read Markdown [%s]: %w\", file.RelPath, err))\n}\n\nfunc validateObsidianReadableFile(file *obsidianSourceFile) error {\n\topened, err := os.Open(file.AbsPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer opened.Close()","sourceCodeStart":925,"sourceCodeEnd":961,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/import_obsidian.go#L925-L961","documentation":"A Vault file changed while it was being read: the post-read os.Stat shows a size or modification time different from the pre-read stat, so the partially-read data is discarded and errObsidianSourceChanged is wrapped with the context 'while reading'. This prevents importing a torn mix of old and new content.","triggerScenarios":"After os.ReadFile succeeds, after.Size() != before.Size() or the ModTime differs — the writer saved the file between the pre-read stat and the post-read stat.","commonSituations":"Obsidian autosave firing mid-import; sync clients replacing files during the scan; editors that write files non-atomically while SiYuan reads them.","solutions":["Close Obsidian/editors and pause sync tools, then retry the import","Import from a static copy of the vault instead of the live directory","Ensure editors save atomically (most do; Obsidian autosave can be disabled temporarily)","Simply re-run the import once the vault is quiet"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := importObsidianVault(vaultPath)\nif errors.Is(err, errObsidianSourceChanged) {\n    err = importObsidianVault(vaultPath) // retry on quiescent vault\n}","preventionTips":["Ensure no writer touches the vault during the import window","Use editors that save atomically (write-temp-then-rename)","Import from a static snapshot when the live vault is busy"],"tags":["obsidian-import","concurrency","torn-read","file-changed"],"backgroundTag":"checksum-mismatch","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"}