siyuan-note/siyuan · error

mkdir notebook crypt backup dir failed: %w

Error message

mkdir notebook crypt backup dir failed: %w

What it means

writeNotebookCryptBackup creates the directory that will hold the notebook's BoxCrypt backup file (via os.MkdirAll with 0755) and wraps any failure with this message. It is an I/O-level failure creating the backup directory, not a cryptographic problem.

Source

Thrown at kernel/model/crypto.go:2529

// 全局备份存 MasterSalt/KEKVerifier,per-notebook 备份存 WrappedDEK/WrapNonce。
const notebookCryptoBackupFilename = "notebook-crypto-backup.json"

func notebookCryptoBackupPath(boxID string) string {
	return filepath.Join(util.DataDir, boxID, ".siyuan", notebookCryptoBackupFilename)
}

// writeNotebookCryptBackup 写入加密笔记本的 BoxCrypt 备份。
// 仅在 Encrypted=true 的笔记本上调用,配合 CreateEncryptedBox / ChangeMasterPassword 写入。
func writeNotebookCryptBackup(boxID string, crypt *conf.BoxEncryption) error {
	if !ast.IsNodeIDPattern(boxID) {
		return errors.New("invalid notebook ID")
	}
	if err := validateBoxEncryption(crypt); err != nil {
		return err
	}
	backupPath := notebookCryptoBackupPath(boxID)
	if err := os.MkdirAll(filepath.Dir(backupPath), 0755); err != nil {
		return fmt.Errorf("mkdir notebook crypt backup dir failed: %w", err)
	}
	data, err := gulu.JSON.MarshalIndentJSON(crypt, "", "  ")
	if err != nil {
		return fmt.Errorf("marshal notebook crypt backup failed: %w", err)
	}
	if err := filelock.WriteFile(backupPath, data); err != nil {
		return fmt.Errorf("write notebook crypt backup failed: %w", err)
	}
	return nil
}

// readNotebookCryptBackup 读取加密笔记本的 BoxCrypt 备份。
// 备份文件不存在时返回 (nil, nil),调用方据此区分"非加密笔记本"和"备份不存在"。
func readNotebookCryptBackup(boxID string) (*conf.BoxEncryption, error) {
	if !ast.IsNodeIDPattern(boxID) {
		return nil, errors.New("invalid notebook ID")
	}
	backupPath := notebookCryptoBackupPath(boxID)

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Check the error wrapped by %w — fix the underlying cause (permissions, disk space, path conflict)
  2. Ensure the workspace data directory is writable by the SiYuan process user
  3. Remove or rename any regular file that occupies the backup directory path
  4. If running in Docker, mount the workspace volume read-write

Example fix

// before
err := model.ChangeMasterPassword(box, old, new) // fails: mkdir ...: permission denied
// after
# fix host permissions first, e.g.:
# chown -R 1000:1000 /siyuan/workspace
err := model.ChangeMasterPassword(box, old, new)
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check writability
probe := filepath.Join(workspace, "data", ".write-test")
if err := os.WriteFile(probe, nil, 0644); err != nil {
    return fmt.Errorf("workspace not writable: %w", err)
}
os.Remove(probe)

Try / catch

if err := model.ChangeMasterPassword(box, old, new); err != nil {
    var perr *fs.PathError
    if errors.As(err, &perr) { log.Printf("fs failure at %s: %v", perr.Path, perr.Err) }
    return err
}

Prevention

When it happens

Trigger: os.MkdirAll(filepath.Dir(backupPath), 0755) fails during CreateEncryptedBox or ChangeMasterPassword — typically because the workspace data directory is read-only, disk is full, the path component exists as a regular file, or permission bits deny creation.

Common situations: SiYuan running with a read-only data directory (Docker volume mounted ro, sync client locking files); a stray file occupying the backup directory path; disk quota exhausted; running on Windows with the folder open/locked by another process.

Understand the failure class

Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/37a6b30b9ede90e0. Report an issue: GitHub.