siyuan-note/siyuan · error

decrypt box [ ] failed: incorrect key or corrupted data

Error message

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

What it means

decryptBoxCrypt returns this error after both the primary WrappedDEK and any differing backup WrappedDEK fail to decrypt with the supplied KEK. It means the derived key does not match the notebook's key wrapping, or the wrapped key data is corrupted. This is the generic 'wrong password or damaged key material' outcome of the per-notebook unlock pipeline.

Solutions

  1. Retry with the correct current master password (each attempt costs ~1s Argon2id)
  2. If a password change was interrupted, complete the migration with the old or new password as appropriate so KEK and WrappedDEK generations match
  3. Restore the notebook's conf.json and crypto backup from a known-good backup/sync peer
  4. If the password is truly lost, the notebook data is unrecoverable by design; restore from an unencrypted snapshot

Example fix

// before
err := model.UnlockBox(boxID, typedPassword, boxEnc)
// treated as fatal, box marked error
// after
if err != nil && strings.Contains(err.Error(), "incorrect key or corrupted data") {
    // prompt user to re-enter password; do not permanently mark state error on first failure
    return retryUnlockWithPrompt(boxID)
}
Defensive patterns

Strategy: try-catch

Try / catch

if err := model.UnlockBox(boxID, password, boxEnc); err != nil {
    if strings.Contains(err.Error(), "incorrect key or corrupted data") {
        // treat as wrong-password: prompt retry, cap attempts
    }
}

Prevention

When it happens

Trigger: UnlockBox/UnlockAndMountBox with a wrong master password; ChangeMasterPassword racing an incomplete migration; WrappedDEK or backup WrappedDEK corrupted (bit rot, manual edit, bad sync); KEK derived from an older password while notebooks were already re-wrapped.

Common situations: User forgot/misspelled the master password; workspace synced mid-password-change so conf and backup hold WrappedDEKs from different password generations; history KEKs are out of sync so decryptWrappedDEKWithHistory cannot unwrap.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/185f2c4f5438326f. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/crypto.go:1355

		dek, err = decryptWrappedDEKWithHistory(boxID, backup, kek, nc)
		if err == nil {
			// backup 解密成功:修复 conf + 刷新 backup
			box := &Box{ID: boxID}
			boxConf := box.GetConf()
			boxConf.Encrypted = true
			boxConf.BoxCrypt = backup
			if saveErr := box.SaveConf(boxConf); saveErr != nil {
				logging.LogWarnf("fix encrypted box conf from backup [%s] failed: %s", boxID, saveErr)
			}
			if needWriteNotebookCryptBackup(boxID, backup) {
				if writeErr := writeNotebookCryptBackup(boxID, backup); writeErr != nil {
					logging.LogWarnf("refresh notebook crypt backup [%s] failed: %s", boxID, writeErr)
				}
			}
			return dek, backup, nil
		}
	}
	return nil, nil, fmt.Errorf("decrypt box [%s] failed: incorrect key or corrupted data", boxID)
}

// UnlockBox 用主密码派生 KEK,解出该笔记本的 DEK 并缓存。KEK 用完即弃,不全局缓存。
// 每次调用都跑一次 Argon2id(约 1 秒),严格满足"每笔记本单独解锁"语义。
func UnlockBox(boxID string, password string, boxEnc *conf.BoxEncryption) (err error) {
	invalidateEncryptedPublishAccessCache()
	if !ast.IsNodeIDPattern(boxID) {
		return errors.New("invalid notebook ID")
	}

	// 全局配置锁先于笔记本生命周期锁获取(设计 §17 锁顺序约定),避免与持子系统锁后回取配置锁的路径死锁。
	// notebookCryptoMu 持锁期间调用的 deriveKEK/conf 修复只申请 Conf.m/cachedDEKsLock,不回取 box 生命周期锁。
	notebookCryptoMu.Lock()
	defer notebookCryptoMu.Unlock()
	releaseTransition := holdEncryptedBoxTransition(boxID)
	defer releaseTransition()
	return unlockBoxHeld(boxID, password, boxEnc)
}

View on GitHub (pinned to 9f775e8a12)