siyuan-note/siyuan · error

Decryption failed: incorrect key or corrupted data [box=%s]

Error message

Decryption failed: incorrect key or corrupted data [box=%s]

What it means

Thrown by ChangeMasterPassword Phase 0 when decryptBoxCrypt(id, oldKEK) fails for a specific encrypted notebook. decryptBoxCrypt tries both the conf.json BoxCrypt and the per-notebook backup; failure means the old password's KEK could not unwrap any WrappedDEK for that notebook. The message includes the box ID for diagnosis (Language(316) + ' [box=<id>]').

Source

Thrown at kernel/model/crypto.go:1730

	}
	newKEK := util.DeriveKey(newPassword, nc.MasterSalt, params)
	defer zeroAndClear(newKEK)
	newVerifier, err := util.EncryptWithAAD(newKEK, kekVerifierMagic, []byte("siyuan:kek-verifier"))
	if err != nil {
		return err
	}

	// Phase 0: 遍历所有加密笔记本(含 conf 损坏但存在备份的),预计算新 WrappedDEK(内存操作)
	// 允许 entries 为空:用户可能已启用加密功能但尚未创建加密笔记本,此时仍需更新全局 verifier 和 backup。
	encBoxIDs, listErr := listAllEncryptedBoxIDs()
	if listErr != nil {
		return fmt.Errorf("list encrypted notebooks failed: %w", listErr)
	}
	var entries []migrationBoxEntry
	for _, id := range encBoxIDs {
		dek, boxCrypt, dErr := decryptBoxCrypt(id, oldKEK)
		if dErr != nil {
			return errors.New(Conf.Language(316) + " [box=" + id + "]")
		}
		newWrapped, nErr := util.EncryptWithAAD(newKEK, dek, wrappedDEKAAD(id))
		if nErr != nil {
			return nErr
		}
		entries = append(entries, migrationBoxEntry{
			BoxID:         id,
			NewSpec:       boxEncryptionSpec,
			NewWrappedDEK: newWrapped,
			NewWrapNonce:  mustEncryptionNonce(newWrapped),
			Metadata:      append([]byte(nil), boxCrypt.Metadata...),
		})
	}

	// Phase 1: 持久化 migration manifest(崩溃后 recovery 的依据)
	newParamsJSON, _ := gulu.JSON.MarshalJSON(params)
	mig := &masterPasswordMigration{
		OldVerifier:      nc.KEKVerifier,

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Verify the old password is correct — try unlocking one of the listed encrypted notebooks with it first.
  2. Identify the problematic box from the [box=<id>] suffix and check if its WrappedDEK matches the current global verifier.
  3. If one notebook has an inconsistent WrappedDEK, restore its conf.json or backup from a device where it's consistent, let sync propagate, then retry the password change.
  4. If a master-password migration is pending from a previous attempt, restart SiYuan to complete recovery first.
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the old password works before starting the migration by
// attempting to derive the KEK (or unlock a test notebook):
// deriveKEK is internal, but you can call UnlockBox on one notebook
// to validate the password, then LockBox before ChangeMasterPassword.

Try / catch

if err := model.ChangeMasterPassword(oldPassword, newPassword); err != nil {
    if strings.Contains(err.Error(), "[box=") {
        // specific notebook failed to decrypt with old KEK
        // extract box ID, check its WrappedDEK consistency
    }
}

Prevention

When it happens

Trigger: Fires during ChangeMasterPassword when the old password is wrong (same KEK can't decrypt any WrappedDEK), or when a specific notebook's WrappedDEK is corrupted/belongs to a different KEK than the others. The error is returned immediately, aborting the migration before any changes are written.

Common situations: User enters the wrong old password. A notebook was encrypted under a different master password (e.g., imported from another workspace). A partial migration on another device left one notebook's WrappedDEK on the old key while the global verifier was already switched. Sync brought an inconsistent WrappedDEK for one notebook.

Related errors


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