siyuan-note/siyuan · error

encrypted repository data has no matching notebook [%s]

Error message

encrypted repository data has no matching notebook [%s]

What it means

decryptRepoDataIfNeeded: the path's boxID segment is a valid node ID, but IsEncryptedBox(boxID) is false AND the bytes are ciphertext. This means the notebook is not registered as encrypted, so there is no DEK to decrypt with — fail-closed. The boxID is included in the message. Contrast with 866 (invalid boxID format) and 868 (valid encrypted box but locked).

Source

Thrown at kernel/model/repository.go:748

// decryptRepoDataIfNeeded 判断仓库数据是否属于加密笔记本,如果是则按路径类型分流解密。
// file.Path 格式:/<boxID>/...
// .sy → DecryptFile,assets/* → DecryptAsset,storage/av/*.json → av.DecryptAVData。
// 密文缺少有效路径上下文、笔记本未解锁或认证失败时返回错误,不允许调用方按明文继续处理。
func decryptRepoDataIfNeeded(data []byte, filePath string) ([]byte, error) {
	relPath := strings.TrimPrefix(filePath, "/")
	parts := strings.SplitN(relPath, "/", 2)
	encryptedPayload := util.IsCiphertext(data) || bytes.HasPrefix(data, encryptedAssetMagic)
	if len(parts) < 2 || !ast.IsNodeIDPattern(parts[0]) {
		if encryptedPayload {
			return nil, errors.New("encrypted repository data is missing notebook context")
		}
		return data, nil
	}
	boxID := parts[0]
	if !IsEncryptedBox(boxID) {
		if encryptedPayload {
			return nil, fmt.Errorf("encrypted repository data has no matching notebook [%s]", boxID)
		}
		return data, nil
	}
	// 持读锁,防止 LockBox 在解密期间清 DEK/缓存
	HoldBoxReadLock(boxID)
	defer ReleaseBoxReadLock(boxID)
	dek, err := GetDEKIfUnlocked(boxID)
	if err != nil {
		return nil, errors.New(Conf.Language(314))
	}
	boxRelPath := parts[1]
	// 按路径类型分流
	if strings.HasPrefix(boxRelPath, "assets/") {
		diskName := filepath.Base(boxRelPath)
		plain, decErr := DecryptAsset(boxID, diskName, dek, data)
		if decErr != nil {
			return nil, decErr
		}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Verify whether the named boxID should be encrypted; if so, restore its .siyuan/conf.json with Encrypted=true and the same key material.
  2. If the notebook genuinely is not encrypted, the ciphertext object is orphaned/corrupt — remove it or rebuild the repo index.
  3. Restore from a consistent snapshot set where path and encryption flag agree.
Defensive patterns

Strategy: validation

Validate before calling

// Reject ciphertext whose boxID is not a registered encrypted notebook
rel := strings.TrimPrefix(filePath, "/")
parts := strings.SplitN(rel, "/", 2)
if len(parts) == 2 && ast.IsNodeIDPattern(parts[0]) && !model.IsEncryptedBox(parts[0]) {
    if util.IsCiphertext(data) || bytes.HasPrefix(data, encryptedAssetMagic) {
        return fmt.Errorf("encrypted repository data has no matching notebook [%s]", parts[0])
    }
}

Prevention

When it happens

Trigger: A snapshot object carries ciphertext for a notebook whose conf.json no longer marks it Encrypted (e.g. it was downgraded/removed), or ciphertext leaked into a normal notebook's path during a botched copy/restore.

Common situations: Notebook un-encrypted or deleted but ciphertext objects remain in the repo store; restoring a backup of conf.json that lost the Encrypted flag; cross-workspace repo copy.

Related errors


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