siyuan-note/siyuan · critical

encrypted notebook metadata verification failed after write

Error message

encrypted notebook metadata verification failed after write

What it means

Returned by CreateEncryptedBox when, after SaveConf and writeNotebookCryptBackup both report success, a readback (box.GetConf) shows the encrypted configuration is NOT actually persisted — Encrypted is false or BoxCrypt is nil. This is a data-integrity safety net: it prevents a notebook from being treated as plain when it should be encrypted (which would cause plaintext writes to what is meant to be an encrypted store).

Source

Thrown at kernel/model/crypto.go:2588

	}

	box := &Box{ID: id}
	boxConf := box.GetConf()
	boxConf.Encrypted = true
	boxConf.BoxCrypt = enc
	if err = encryptBoxMetadata(id, boxConf, dek); err != nil {
		return "", fmt.Errorf("encrypt notebook metadata failed: %w", err)
	}
	if err = box.SaveConf(boxConf); err != nil {
		return "", fmt.Errorf("save encrypted notebook conf failed: %w", err)
	}
	if err = writeNotebookCryptBackup(id, enc); err != nil {
		return "", fmt.Errorf("write notebook crypt backup failed: %w", err)
	}
	// 回读校验加密配置已落盘,避免写失败后按普通笔记本处理
	verifyConf := box.GetConf()
	if verifyConf == nil || !verifyConf.Encrypted || verifyConf.BoxCrypt == nil {
		err = errors.New("encrypted notebook metadata verification failed after write")
		return "", err
	}
	markRuntimeEncryptedBox(id)
	invalidateEncryptedPublishAccessCache()

	// 复用刚派生的 DEK 直接开 db + 缓存,省去再次 Argon2id 解锁
	cachedDEKsLock.Lock()
	if err = sql.OpenEncryptedDB(id, dek); err != nil {
		cachedDEKsLock.Unlock()
		return "", err
	}
	if err = treenode.OpenEncryptedBlockTreeDB(id, dek); err != nil {
		sql.CloseEncryptedDB(id)
		cachedDEKsLock.Unlock()
		return "", err
	}
	cachedDEKs[id] = dek
	cachedDEKsLock.Unlock()

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Ensure only one kernel instance is running against the workspace.
  2. Check for concurrent code paths that write to the same notebook's .syconf and serialize them with notebookCryptoMu or a per-box lock.
  3. Examine filesystem sync semantics — if using a network FS, ensure writes are fsync'd before the readback.
Defensive patterns

Strategy: validation

Try / catch

id, err := model.CreateEncryptedBox(name, password)
if err != nil {
    if strings.Contains(err.Error(), "verification failed after write") {
        // integrity violation — possible concurrent writer or FS caching issue
        logging.LogErrorf("critical: encrypted conf did not persist for box %s", id)
    }
    return err
}

Prevention

When it happens

Trigger: SaveConf reported success but the file wasn't actually written or was overwritten by another process between write and readback. A race with a concurrent conf writer, a stale file-cache, or a filesystem that acknowledged a write before flushing.

Common situations: Concurrent operations modifying the same notebook's .syconf. Filesystem caching layer that delays persistence. Another kernel instance writing to the same workspace (unsupported but happens in misconfigured setups).

Related errors


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