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) {
		return

View on GitHub (pinned to afa823b6b4)

Solutions

  1. 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)
  2. Fix write permissions and free disk space on the workspace volume as with any mkdir failure
  3. 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

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


AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/344f26dba31cdf7d. Report an issue: GitHub.