{"record":{"id":"aa2ba0aa7b2f5c77","repo":"siyuan-note/siyuan","slug":"read-document-s-failed-w","errorCode":null,"errorMessage":"read document [%s] failed: %w","messagePattern":"read document \\[(.+?)\\] failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/history.go","lineNumber":978,"sourceCode":"\tluteEngine := util.NewLute()\n\tfor _, file := range files {\n\t\tif err = generateDocHistoryFile(box.ID, file, historyDir, luteEngine); err != nil {\n\t\t\tlogging.LogErrorf(\"generate history failed: %s\", err)\n\t\t\treturn\n\t\t}\n\t}\n\n\tindexHistoryDir(filepath.Base(historyDir), util.NewLute())\n\treturn\n}\n\nfunc generateDocHistoryFile(boxID, file, historyDir string, luteEngine *lute.Lute) error {\n\tdata, err := filelock.ReadFile(file)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"read document [%s] failed: %w\", file, err)\n\t}\n\n\treturn generateDocHistoryFromData(boxID, file, historyDir, data, luteEngine)\n}\n\nfunc generateDocHistoryFromData(boxID, file, historyDir string, data []byte, luteEngine *lute.Lute) error {\n\thistoryPath := filepath.Join(historyDir, boxID, strings.TrimPrefix(file, filepath.Join(util.DataDir, boxID)))\n\tif err := os.MkdirAll(filepath.Dir(historyPath), 0755); err != nil {\n\t\treturn err\n\t}\n\tif err := gulu.File.WriteFileSafer(historyPath, data, 0644); err != nil {\n\t\treturn err\n\t}\n\tif !strings.HasSuffix(file, \".sy\") {\n\t\treturn nil\n\t}\n\n\ttree, err := loadTreeByData(file, data, luteEngine)","sourceCodeStart":960,"sourceCodeEnd":996,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/history.go#L960-L996","documentation":"generateDocHistoryFile reads a document .sy file before generating its history snapshot. If the read fails with an error other than os.IsNotExist (a missing file is intentionally treated as nothing-to-do), the failure is wrapped as \"read document [%s] failed\" including the file path. This guards the doc-history pipeline against unreadable or corrupted .sy sources.","triggerScenarios":"generateDocHistory0 (invoked during doc-history generation, e.g. on document update/delete) hitting a .sy file that exists but cannot be read: permission denied, IO error, file locked, or truncated/corrupted content returning a non-NotExist error.","commonSituations":"Workspace files copied as root changing ownership; sync conflicts leaving unreadable .sy files; disk errors; antivirus locking the file during read; mobile/mobile-storage IO glitches.","solutions":["Check filesystem permissions/ownership of the .sy file named in the message and restore read access","Verify disk health and free space; retry the operation after transient IO errors","Restore the .sy file from sync/backup if it is corrupted; missing files are tolerated but unreadable ones are not","Re-index or re-open the notebook so history generation retries with a valid document tree"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const fs = require(\"fs\");\ntry {\n  fs.accessSync(syPath, fs.constants.R_OK);\n} catch (e) {\n  if (e.code === \"ENOENT\") return; // tolerated by the kernel\n  throw new Error(\"document unreadable: \" + syPath);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await triggerDocHistory(boxID, syPath);\n} catch (e) {\n  if (/read document \\[.+\\] failed/.test(e.message)) {\n    // restore the .sy file from sync/backup and retry\n  }\n  throw e;\n}","preventionTips":["Keep workspace files owned/readable by the kernel process user","Restore corrupted .sy files from sync or history before editing again","Avoid writing .sy files with external tools while the kernel is running"],"tags":["history","document","file-read","io"],"backgroundTag":"file-read-failed","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}