siyuan-note/siyuan · error
create history directory [%s] failed: %w
Error message
create history directory [%s] failed: %w
What it means
createAssetsHistory (kernel/model/history.go:898-901) fails when os.MkdirAll cannot create the per-asset subdirectory inside the history tree; the target directory name is included in the message and the underlying error is chained with %w. Same environmental causes as any MkdirAll failure: permission denied, disk full, or a regular file occupying a path where a directory is needed.
Source
Thrown at kernel/model/history.go:900
return errors.New("asset path must be a file")
}
return createAssetsHistory([]string{assetAbsPath})
}
func createAssetsHistory(assets []string) (err error) {
historyDir, err := getHistoryDir(HistoryOpUpdate)
if err != nil {
return fmt.Errorf("get history directory failed: %w", err)
}
for _, file := range assets {
assetRelPath, relErr := filepath.Rel(filepath.Join(util.DataDir, "assets"), file)
if relErr != nil || assetRelPath == "." || strings.HasPrefix(assetRelPath, ".."+string(filepath.Separator)) {
return errors.New("asset path must be under assets")
}
historyPath := filepath.Join(historyDir, "assets", assetRelPath)
if err = os.MkdirAll(filepath.Dir(historyPath), 0755); err != nil {
return fmt.Errorf("create history directory [%s] failed: %w", filepath.Dir(historyPath), err)
}
if err = filelock.Copy(file, historyPath); err != nil {
if os.IsNotExist(err) {
continue
}
return fmt.Errorf("copy asset [%s] to [%s] failed: %w", file, historyPath, err)
}
}
indexHistoryDir(filepath.Base(historyDir), util.NewLute())
return
}
func (box *Box) generateDocHistory0() {
files := box.recentModifiedDocs()
if 1 > len(files) {
returnView on GitHub (pinned to afa823b6b4)
Solutions
- Inspect the directory named in the error; if a regular file occupies the path, remove or rename it (with the kernel stopped if it is in use)
- Fix write permissions and free disk space on the workspace volume as with any mkdir failure
- After repairing the tree, trigger /api/history/reindexHistory so the history index matches the fixed layout
Defensive patterns
Strategy: try-catch
Validate before calling
if fi, err := os.Stat(filepath.Dir(targetHistoryPath)); err == nil && !fi.IsDir() {
return fmt.Errorf("regular file occupies directory path: %s", filepath.Dir(targetHistoryPath))
} Try / catch
err := createAssetsHistory(files)
if err != nil && strings.Contains(err.Error(), "create history directory") {
if errors.Is(err, fs.ErrExist) || errors.Is(err, fs.ErrNotDir) { /* remove conflicting file entry */ }
if errors.Is(err, fs.ErrPermission) { /* fix perms */ }
} Prevention
- Avoid asset names that alternate between file and directory roles in the same path
- Keep history/ writable and disk-space monitored
- Reindex history after repairing the tree
When it happens
Trigger: A previous history run left a regular file where a directory must now be created (e.g. history/<ts>-update/assets/foo exists as a file while a new snapshot needs history/.../assets/foo/bar.png); history/ not writable; ENOSPC while creating deep asset subpaths.
Common situations: Assets whose names collide across file/dir roles over time (photo.png as file, then photo.png/ as dir for child assets); permission or disk-space regressions between generation cycles.
Related errors
- get history directory failed: %w
- failed to make directory: %w
- invalid frontend capability ID: %s
- unsupported agent runtime schema version: %d
- write data [%s] failed: %s
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/344f26dba31cdf7d.
Report an issue: GitHub.