siyuan-note/siyuan · error

mkdir box conf dir failed: %w

Error message

mkdir box conf dir failed: %w

What it means

In saveConf0, os.MkdirAll(<DataDir>/<boxID>/.siyuan, 0755) is attempted; on failure it returns fmt.Errorf('mkdir box conf dir failed: %w', err). This creates the per-notebook .siyuan metadata directory before writing conf.json. It fails on permission denied, a read-only filesystem, ENOSPC, or when a non-directory file already exists at that path.

Source

Thrown at kernel/model/box.go:325

		return err
	}
	return syncBoxConfCryptoBackup(box.ID, persisted)
}

func syncBoxConfCryptoBackup(boxID string, boxConf *conf.BoxConf) error {
	if !boxConf.Encrypted || boxConf.BoxCrypt == nil {
		return nil
	}
	if needWriteNotebookCryptBackup(boxID, boxConf.BoxCrypt) {
		return writeNotebookCryptBackup(boxID, boxConf.BoxCrypt)
	}
	return nil
}

func (box *Box) saveConf0(data []byte) error {
	confPath := filepath.Join(util.DataDir, box.ID, ".siyuan/conf.json")
	if err := os.MkdirAll(filepath.Join(util.DataDir, box.ID, ".siyuan"), 0755); err != nil {
		return fmt.Errorf("mkdir box conf dir failed: %w", err)
	}
	if err := filelock.WriteFile(confPath, data); err != nil {
		util.ReportFileSysFatalError(err)
		return fmt.Errorf("write box conf [%s] failed: %w", confPath, err)
	}
	invalidateEncryptedPublishAccessCache()
	return nil
}

// validateBoxPath 校验 box 内相对路径,拒绝 .. 和绝对路径,确保最终路径在 <DataDir>/<boxID>/ 内。
func (box *Box) validateBoxPath(p string) (string, error) {
	return filesys.ValidateBoxRelativePath(box.ID, p)
}

func (box *Box) Ls(p string) (ret []*FileInfo, totals int, err error) {
	if _, err = box.validateBoxPath(p); err != nil {
		return
	}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Check write permissions and free space on the data volume (the workspace dir reported at boot).
  2. If a file occupies <DataDir>/<boxID>/.siyuan, remove it so the directory can be created.
  3. Run SiYuan under a user that owns (or can write) the workspace directory.
  4. For read-only deployments, point --workspace at a writable location.

Example fix

// before: assuming .siyuan always exists
if err := box.saveConf0(data); err != nil { return err }

// after: preflight the metadata dir and report a clearer error
dir := filepath.Join(util.DataDir, box.ID, ".siyuan")
if info, e := os.Stat(dir); e == nil && !info.IsDir() {
    return fmt.Errorf("%s exists but is not a directory; remove it and retry", dir)
}
if err := box.saveConf0(data); err != nil { return err }
Defensive patterns

Strategy: validation

Validate before calling

// Preflight the .siyuan metadata dir before saving.
dir := filepath.Join(util.DataDir, box.ID, ".siyuan")
if info, err := os.Stat(dir); err == nil && !info.IsDir() {
    return fmt.Errorf("%s is a file, not a directory", dir)
}

Try / catch

// Report mkdir failures with the underlying OS error for fast diagnosis.
if err := box.SaveConf(conf); err != nil {
    if strings.Contains(err.Error(), "mkdir box conf dir") {
        log.Errorw("mkdir failed", "box", box.ID, "root", errors.Unwrap(err))
    }
}

Prevention

When it happens

Trigger: Box.SaveConf on a notebook whose <DataDir>/<boxID> is not writable: SiYuan's data dir on a volume mounted read-only, permissions stripped, a regular file named '.siyuan' left there, or the disk is full.

Common situations: Running the kernel under a user without write access to the workspace; containerized deployment with an unmounted/broken volume; a previous crash left a '.siyuan' file instead of a directory; shared filesystem quota exhausted.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/4e39c23b7d9bacdd. Report an issue: GitHub.