siyuan-note/siyuan · error
encrypted repository data is missing notebook context
Error message
encrypted repository data is missing notebook context
What it means
decryptRepoDataIfNeeded rejects encrypted payloads (ciphertext or encrypted-asset magic) when the file path cannot be split into '<boxID>/<relpath>' with a valid node-ID boxID. Without a well-formed notebook path prefix there is no way to select the right notebook key, so it returns 'encrypted repository data is missing notebook context'.
Solutions
- Pass the full repo-relative path including the notebook-ID first segment to the repo API
- Re-create or re-index the affected snapshot so paths carry the boxID prefix
- If the encrypted payload legitimately has no notebook context, this data is unreadable by design — remove it from the repo or re-store it unencrypted
- Verify callers don't TrimPrefix or rewrite the path before invoking repo file APIs
Example fix
// before: path lost the notebook prefix
await fetchPost('/api/repo/getRepoFile', { path: 'assets/foo.png' });
// after: keep the boxID segment
await fetchPost('/api/repo/getRepoFile', { path: '20240101120000-abcdefg/assets/foo.png' }); Defensive patterns
Strategy: validation
Validate before calling
const rel = repoPath.replace(/^\//, '');
const m = rel.match(/^([0-9]{14}-[0-9a-z]{7})\//);
if (!m) throw new Error('Encrypted repo path needs a notebook-ID prefix: ' + repoPath); Type guard
const hasBoxPrefix = (p) => /^\/?[0-9]{14}-[0-9a-z]{7}\//.test(p); Try / catch
try {
return await fetchPost('/api/repo/getRepoFile', { path: repoPath });
} catch (e) {
if (String(e).includes('missing notebook context')) logUnreadableEntry(repoPath);
else throw e;
} Prevention
- Never strip the leading notebook-ID segment from repo paths
- Use paths exactly as returned by repo listing APIs
- Re-index after unusual workspace manipulations to rebuild snapshot paths
- Treat root-level encrypted entries as corruption to be re-snapshotted
When it happens
Trigger: Calling any path-based repo reader (GetRepoFile, OpenRepoSnapshotFile, RollbackRepoSnapshotFile, ExportRepoFile, parseTitleInSnapshot) with an encrypted payload whose path has no directory prefix or whose first segment is not a SiYuan node-ID pattern (e.g. root-level files like 'index.json' that are somehow encrypted).
Common situations: Snapshots containing top-level non-document files that were encrypted by mistake; tooling that strips the first path component before calling the API; manually constructed repo paths that omit the notebook-ID segment; legacy snapshots from format transitions.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- encrypted repository data is missing valid notebook context
- encrypted repository data has no matching notebook
- cannot replay block swap across encrypted notebook…
- cannot swap blocks across encrypted notebook boundaries
- CLI does not support encrypted notebook
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/137d2d4384429836.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/repository.go:724
}
title = tree.Root.IALAttr("title")
rootID = tree.Root.ID
}
return
}
// 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]View on GitHub (pinned to 9f775e8a12)