siyuan-note/siyuan · error
block not found or its encrypted notebook is locked
Error message
block not found or its encrypted notebook is locked
What it means
holdEncryptedBlockRequests resolves each block ID via treenode.GetBlockTree(id); when a block cannot be resolved and allowMissing is false, it rejects the whole request with this message. The ID either does not exist at all, or it belongs to an encrypted notebook that is currently locked (so the block index is unavailable), and proceeding without a lease would allow un-leased access to that box.
Source
Thrown at kernel/api/box_lease.go:71
}
// holdEncryptedBlockRequests 按块的实际归属取得响应租约,覆盖省略笔记本参数的通用和批量读取。
func holdEncryptedBlockRequests(c *gin.Context, boxID string, ids []string, allowMissing bool) error {
if boxID != "" {
return holdEncryptedBoxRequest(c, boxID)
}
boxIDSet := map[string]struct{}{}
for _, id := range ids {
if !ast.IsNodeIDPattern(id) {
continue
}
block := treenode.GetBlockTree(id)
if block == nil {
if allowMissing {
continue
}
// 查不到的块直接拒绝,防止随后解锁的笔记本在第二次查找时被无租约访问。
return errors.New("block not found or its encrypted notebook is locked")
}
if model.IsEncryptedBox(block.BoxID) {
boxIDSet[block.BoxID] = struct{}{}
}
}
boxIDs := make([]string, 0, len(boxIDSet))
for id := range boxIDSet {
boxIDs = append(boxIDs, id)
}
sort.Strings(boxIDs)
for _, id := range boxIDs {
if err := holdEncryptedBoxRequest(c, id); err != nil {
return err
}
}
return nil
}
View on GitHub (pinned to 8641553a1f)
Solutions
- Unlock the encrypted notebook that owns the referenced block, then retry
- Validate every block ID exists (getBlockInfo) before sending the batch; drop unknown IDs if your caller supports allowMissing semantics
- Refresh stale IDs from current document state after sync deletions
- Correct ID formatting/typos (22-char IDs, no whitespace)
Example fix
// before
await api.setBlockAttrs(ids); // ids includes a deleted one
// after
const valid = [];
for (const id of ids) {
if ((await fetchSyncPost("/api/block/getBlockInfo", { id })).code === 0) valid.push(id);
}
await api.setBlockAttrs(valid); Defensive patterns
Strategy: validation
Validate before calling
for (const id of ids) {
const r = await fetchSyncPost("/api/block/getBlockInfo", { id });
if (r.code !== 0) throw new Error(`Unknown or locked block: ${id}`);
} Try / catch
try { await batchApi(ids); } catch (e) { if (String(e).includes("encrypted notebook is locked")) { await promptUnlock(); retry(validIds); } } Prevention
- Pre-validate every ID in batches
- Unlock all referenced encrypted notebooks first
- Prune stale IDs after sync-deletion push events
When it happens
Trigger: holdBlockRequest processing a batch of block IDs where at least one ID is not in blocktree (deleted block, bad ID) or lives in a locked encrypted notebook, and the call does not set allowMissing.
Common situations: Batch API request contains one stale ID from a synced deletion; an encrypted notebook is locked while other requests in the batch target unlocked notebooks; ID typo or truncated ID in a plugin; blocktree not yet built for a newly opened box.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- encrypted notebook is locked, please unlock it first
- master password migration is pending
- Please unlock the encrypted notebook first
- Please unlock the encrypted notebook first
- get block failed:
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/fb08fa9d68651240.
Report an issue: GitHub.