siyuan-note/siyuan · critical
317
317
Error message
Invalid key backup file
What it means
Thrown by deriveNotebookCryptoBackupCandidate (crypto.go:1176, i18n code 317) when the backup exists and has key fields but its Argon2 KDF parameters fail validation (util.ValidateArgon2Params). Invalid KDF params mean the backup cannot safely derive the KEK, so it is rejected as an invalid key backup file rather than risking a weak or malformed key derivation.
Source
Thrown at kernel/model/crypto.go:1176
// 调用方已持有 notebookCryptoMu,且 writeNotebookCryptoBackupData 不再申请该锁,故无死锁;
// 同步写避免与 ChangeMasterPassword 的并发备份写竞争同一文件(lost update 导致 verifier 被回退)。
nc := *backup
if err := writeNotebookCryptoBackupData(&nc, kek); err != nil {
logging.LogWarnf("rewrite notebook crypto backup after restore failed: %s", err)
}
logging.LogInfof("notebook crypto restored from backup (e.g. after sync to a new device)")
return kek, nil
}
// deriveNotebookCryptoBackupCandidate 对同步备份做无副作用验证,并确认它覆盖全部现有加密笔记本。
func deriveNotebookCryptoBackupCandidate(password string) (backup *conf.NotebookCrypto, kek []byte, err error) {
backup, err = loadNotebookCryptoBackup()
if err != nil || backup == nil || len(backup.MasterSalt) == 0 || len(backup.KEKVerifier) == 0 {
return nil, nil, errors.New(Conf.Language(310))
}
params, validErr := util.ValidateArgon2Params(backup.KDFParams)
if validErr != nil {
return nil, nil, errors.New(Conf.Language(317))
}
kek = util.DeriveKey(password, backup.MasterSalt, params)
decrypted, decryptErr := util.DecryptWithAAD(kek, backup.KEKVerifier, []byte("siyuan:kek-verifier"))
if decryptErr != nil || string(decrypted) != string(kekVerifierMagic) {
zeroAndClear(kek)
return nil, nil, errors.New(Conf.Language(311))
}
if backup.Spec != conf.CurrentNotebookCryptoSpec || backup.Checksum == "" ||
len(backup.KEKMAC) == 0 || !verifyKEKMAC(backup, kek) {
zeroAndClear(kek)
return nil, nil, errors.New(Conf.Language(316))
}
if !verifyKEKAgainstExistingBoxes(kek) || !verifyKEKAgainstEncryptedHistory(kek) {
zeroAndClear(kek)
return nil, nil, errors.New(Conf.Language(316))
}
backup.KDFParams = params
return backup, kek, nilView on GitHub (pinned to 251596fc0d)
Solutions
- Restore a backup with valid KDFParams from a known-good sync snapshot or another device, then retry.
- If only conf.json has valid KDFParams, prefer the local-config auth path (do not force the backup candidate path).
- As a last resort, if the encrypted data is expendable, remove orphaned notebooks/history and re-enable with a fresh key domain.
Defensive patterns
Strategy: validation
Validate before calling
// Confirm backup KDF params are valid before using it for recovery.
b, err := loadNotebookCryptoBackup()
if err != nil || b == nil {
return errors.New("no backup")
}
if _, verr := util.ValidateArgon2Params(b.KDFParams); verr != nil {
return fmt.Errorf("backup KDF params invalid; restore a compatible backup: %w", verr)
} Try / catch
if err := model.EnableEncryptedNotebook(password); err != nil {
if err.Error() == model.Conf.Language(317) {
respond(c, "key backup has invalid KDF params; restore a spec-compatible backup")
return
}
respond(c, err.Error())
} Prevention
- Do not hand-edit backup KDF params.
- Use a kernel version compatible with the backup's spec/KDF format.
- Keep a known-good backup copy for recovery.
When it happens
Trigger: deriveNotebookCryptoBackupCandidate loads a backup whose KDFParams field is empty, malformed, or out of the allowed Argon2id bounds (memory, iterations, parallelism). Reached on the same recovery paths as 606: re-enable, new-device sync restore, or deriveKEK fallback.
Common situations: Backup written by an older/newer kernel version with different KDF param schema. Backup edited or corrupted. KDF params tampered with. Migration between Argon2 param formats.
Related errors
- 310
- 316
- Argon2id KeyLength must be 32
- Argon2id Memory too low (minimum 64 MB)
- Argon2id Memory too high (maximum 256 MB)
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/18c988e14f0777f8.
Report an issue: GitHub.