siyuan-note/siyuan · error

encrypted repository data has no matching notebook

Error message

encrypted repository data has no matching notebook [%s]

What it means

decryptRepoDataIfNeeded found a syntactically valid boxID in the path, but IsEncryptedBox(boxID) is false while the payload is ciphertext — the notebook the path points to is not the encrypted notebook that owns this data. It returns 'encrypted repository data has no matching notebook [%s]' naming the offending boxID.

Solutions

  1. Confirm the notebook with that ID exists and is configured as encrypted (IsEncryptedBox); restore it if deleted
  2. If the notebook was intentionally de-encrypted, re-snapshot or remove the stale encrypted entries
  3. Sync completely so the encrypted box's metadata exists locally before reading its history
  4. Verify the boxID used in the request path actually matches the snapshot's owner notebook

Example fix

// before: guessing the box ID from a renamed notebook
await fetchPost('/api/repo/getRepoFile', { path: oldBoxID + '/assets/img.png' });
// after: resolve the current box ID from the notebook list first
const nbs = await fetchPost('/api/notebook/lsNotebooks', {});
const box = nbs.data.notebooks.find(n => n.name === 'My Encrypted Notebook');
await fetchPost('/api/repo/getRepoFile', { path: box.id + '/assets/img.png' });
Defensive patterns

Strategy: validation

Validate before calling

const nbs = await fetchPost('/api/notebook/lsNotebooks', {});
const boxID = repoPath.replace(/^\//, '').split('/')[0];
if (!nbs.data.notebooks.some(n => n.id === boxID)) {
  throw new Error('Notebook ' + boxID + ' does not exist locally');
}

Try / catch

try {
  return await fetchPost('/api/repo/getRepoFile', { path: repoPath });
} catch (e) {
  if (String(e).includes('no matching notebook')) await restoreNotebookThenRetry(boxID);
  else throw e;
}

Prevention

When it happens

Trigger: Reading a repo file whose path's first segment parses as a node ID but maps to a plain (non-encrypted) notebook, a deleted notebook, or a box whose encryption metadata is absent, while the payload itself is ciphertext.

Common situations: Copying snapshot data between workspaces with different notebooks; notebooks converted from encrypted to plain (or re-created with the same name but new ID) leaving stale history entries; synced snapshots arriving before the encrypted notebook's metadata; typo'd or fabricated paths in API scripts.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at kernel/model/repository.go:731

// 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 9f775e8a12)