{"record":{"id":"745ec3feafecc433","repo":"siyuan-note/siyuan","slug":"get-history-directory-failed-w","errorCode":null,"errorMessage":"get history directory failed: %w","messagePattern":"get history directory failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/history.go","lineNumber":890,"sourceCode":"\tassetAbsPath := filepath.Join(util.DataDir, filepath.FromSlash(assetPath))\n\tassetsDir := filepath.Join(util.DataDir, \"assets\")\n\tif !gulu.File.IsSubPath(assetsDir, assetAbsPath) {\n\t\treturn errors.New(\"asset path must be under assets\")\n\t}\n\tinfo, statErr := os.Stat(assetAbsPath)\n\tif statErr != nil {\n\t\treturn statErr\n\t}\n\tif info.IsDir() {\n\t\treturn errors.New(\"asset path must be a file\")\n\t}\n\treturn createAssetsHistory([]string{assetAbsPath})\n}\n\nfunc createAssetsHistory(assets []string) (err error) {\n\thistoryDir, err := getHistoryDir(HistoryOpUpdate)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get history directory failed: %w\", err)\n\t}\n\n\tfor _, file := range assets {\n\t\tassetRelPath, relErr := filepath.Rel(filepath.Join(util.DataDir, \"assets\"), file)\n\t\tif relErr != nil || assetRelPath == \".\" || strings.HasPrefix(assetRelPath, \"..\"+string(filepath.Separator)) {\n\t\t\treturn errors.New(\"asset path must be under assets\")\n\t\t}\n\t\thistoryPath := filepath.Join(historyDir, \"assets\", assetRelPath)\n\t\tif err = os.MkdirAll(filepath.Dir(historyPath), 0755); err != nil {\n\t\t\treturn fmt.Errorf(\"create history directory [%s] failed: %w\", filepath.Dir(historyPath), err)\n\t\t}\n\n\t\tif err = filelock.Copy(file, historyPath); err != nil {\n\t\t\tif os.IsNotExist(err) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"copy asset [%s] to [%s] failed: %w\", file, historyPath, err)\n\t\t}","sourceCodeStart":872,"sourceCodeEnd":908,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/model/history.go#L872-L908","documentation":"createAssetsHistory (kernel/model/history.go:888-891) wraps the failure of getHistoryDir(HistoryOpUpdate), which is just os.MkdirAll(history/<yyyyMMdd-HHmmss>-update) (history.go:1161-1168). The %w chain preserves the underlying filesystem error, so the real cause — usually EACCES, EROFS, or ENOSPC — rides along in errors.Unwrap.","triggerScenarios":"The workspace history/ directory is not writable by the kernel process (wrong owner after a copy, root-owned from a Docker volume), the volume is mounted read-only, or the disk is full when the daily asset history generation or an explicit createAssetHistory call runs.","commonSituations":"Docker installs with incorrect volume ownership; workspaces migrated between users preserving bad permissions; full disks during large media snapshots; SELinux/AppArmor denials on the workspace.","solutions":["Inspect the wrapped error (errors.Unwrap / errors.Is fs.ErrPermission, fs.ErrNotExist) to identify EACCES vs ENOSPC vs EROFS","Fix ownership and permissions of <workspace>/history so the kernel user can create directories (0755 and writable)","Free disk space or enlarge the volume, then retry — the next generation cycle recreates the dir","If the volume is read-only by mistake, remount it rw and restart the kernel"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if err := ensureDirWritable(filepath.Join(util.WorkspaceDir, \"history\")); err != nil {\n    return fmt.Errorf(\"history dir not writable, fix before snapshot: %w\", err)\n}\nreturn model.CreateAssetHistory(assetPath)\n\nfunc ensureDirWritable(dir string) error {\n    if err := os.MkdirAll(dir, 0755); err != nil {\n        return err\n    }\n    probe := filepath.Join(dir, \".probe\")\n    if err := os.WriteFile(probe, nil, 0644); err != nil {\n        return err\n    }\n    return os.Remove(probe)\n}","typeGuard":null,"tryCatchPattern":"err := model.CreateAssetHistory(p)\nif err != nil && strings.Contains(err.Error(), \"get history directory failed\") {\n    if errors.Is(err, fs.ErrPermission) { /* fix ownership of history/ */ }\n    if errors.Is(err, fs.ErrNoSpace) { /* free disk and retry */ }\n}","preventionTips":["Monitor free space on the workspace volume","Verify the kernel process owns/can write history/ after migrations and container restarts","Probe writability at startup in ops scripts"],"tags":["filesystem","permissions","disk-full","history","mkdir"],"backgroundTag":"directory-create-failed","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}