siyuan-note/siyuan · critical

Conf.Language(316) + " [box=" + id + "]"

Error message

Conf.Language(316) + " [box=" + id + "]"

What it means

During master-password migration Phase 0, each encrypted notebook's BoxCrypt is decrypted with the old KEK via decryptBoxCrypt(). If decryption fails for a box, migration aborts with a localized message (Conf.Language(316): 'Decryption failed: incorrect key or corrupted data') plus the box ID. The migration refuses to proceed because it cannot re-wrap a DEK it cannot decrypt.

Solutions

  1. Retry the migration with the correct ORIGINAL master password (the one the box was wrapped under)
  2. Identify the failing box from the [box=ID] suffix and check its .siyuan/conf.json and notebook crypt backup for corruption
  3. If the notebook data is still accessible, decrypt/unlock it manually and re-encrypt under the current password before migrating
  4. Restore the affected notebook's conf/backup from a known-good sync or backup snapshot, then retry

Example fix

// before: box wrapped under old KEK blocks migration
ChangeMasterPassword(currentPW, newPW) // -> Decryption failed: incorrect key or corrupted data [box=20240101120000-abcd123]
// after: unlock/re-encrypt the box with the password it was created under, or restore its backup, then rerun ChangeMasterPassword
Defensive patterns

Strategy: validation

Validate before calling

// Ensure every encrypted box decrypts under the current password before migrating
for _, id := range encBoxIDs {
    if _, _, err := decryptBoxCrypt(id, oldKEK); err != nil {
        return fmt.Errorf("box %s not decryptable with current password; resolve before migration", id)
    }
}

Try / catch

if err := model.ChangeMasterPassword(old, new); err != nil {
    if strings.Contains(err.Error(), "Decryption failed") && strings.Contains(err.Error(), "[box=") {
        boxID := extractBetween(err.Error(), "[box=", "]")
        // recover that box: correct password / restore its conf+backup from snapshot
    }
}

Prevention

When it happens

Trigger: ChangeMasterPassword iterating encBoxIDs calls decryptBoxCrypt(id, oldKEK); it fails when the notebook's WrappedDEK/backup was encrypted with a different KEK, the stored key material is corrupted, or the old password/KEK supplied does not match the one the box was wrapped under.

Common situations: User changed the master password previously and some notebook still holds key material wrapped under the older KEK; per-notebook conf or backup corrupted by an interrupted write; restoring a notebook from an old backup into a workspace with a newer master password.

Related errors


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

Appendix: source

Thrown at kernel/model/crypto.go:1757

	if err != nil {
		return err
	}
	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))
		zeroAndClear(dek)
		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{

View on GitHub (pinned to 9f775e8a12)