siyuan-note/siyuan · error
path belongs to encrypted notebook [%s]: %s
Error message
path belongs to encrypted notebook [%s]: %s
What it means
`resolvePath` detected via `rejectEncryptedPath` that the resolved absolute path belongs to an encrypted notebook. MCP file tools never read or write inside an encrypted box — doing so would either leak ciphertext or corrupt the encrypted format by writing plaintext — so the path is refused regardless of lock state.
Source
Thrown at kernel/mcp/tools/file.go:96
return fileFind(args)
case "stat":
return fileStat(args)
}
return CallToolResult{
Content: []ContentItem{{Type: "text", Text: "unknown action '" + action + "', expected one of: [list, read, write, delete, rename, copy, grep, find, stat]"}},
IsError: true,
}, nil
}
func resolvePath(rel string) (string, error) {
rel = filepath.Clean(strings.ReplaceAll(rel, "/", string(os.PathSeparator)))
abs := filepath.Join(util.WorkspaceDir, rel)
if !gulu.File.IsSubPath(util.WorkspaceDir, abs) {
return "", fmt.Errorf("path escapes workspace: %s", rel)
}
// 拒绝加密笔记本目录:MCP 文件工具不能读写加密 box 下的文件(防止密文泄漏或明文破坏加密格式)
if boxID, encrypted := rejectEncryptedPath(abs); encrypted {
return "", fmt.Errorf("path belongs to encrypted notebook [%s]: %s", boxID, rel)
}
// 防止 symlink 逃逸工作区:解析符号链接后再次检查
if resolved := util.ResolveLongestExistingParent(abs); resolved != abs && !gulu.File.IsSubPath(util.WorkspaceDir, resolved) {
return "", fmt.Errorf("symlink escapes workspace: %s", rel)
}
// 禁止访问敏感文件(conf/conf.json、data/snippets/conf.json、data/templates、data/.siyuan/publishAccess.json),
// 与 HTTP 文件 API 共用同一黑名单(见 kernel/util/path_guard.go 的 IsForbiddenAbsPath)
if util.IsForbiddenAbsPath(abs) {
return "", fmt.Errorf("access to sensitive workspace file is forbidden: %s", rel)
}
return abs, nil
}
// rejectEncryptedPath 检查路径是否属于加密笔记本(含 symlink 绕过),返回 boxID 和是否为加密 box。
func rejectEncryptedPath(absPath string) (boxID string, encrypted bool) {
boxID = model.EncryptedRawPathBoxID(absPath)
return boxID, boxID != ""
}View on GitHub (pinned to 251596fc0d)
Solutions
- Use the dedicated block/content tools (which respect encryption and unlock state) instead of raw file access for encrypted notebooks.
- Unlock the notebook and operate through the documented block API rather than the file tool.
- Point the file tool at a non-encrypted location.
Defensive patterns
Strategy: validation
Validate before calling
// Block encrypted-notebook paths before calling the file tool.
if boxID := model.EncryptedRawPathBoxID(abs); boxID != "" {
return "", fmt.Errorf("path belongs to encrypted notebook [%s]", boxID)
} Prevention
- Never edit encrypted-box files via the raw file tool.
- Use the block API, which respects encryption and unlock state.
- Keep encrypted notebook IDs out of file-tool path arguments.
When it happens
Trigger: A file tool `path` argument resolves under an encrypted notebook's data directory; `model.EncryptedRawPathBoxID(abs)` returns a non-empty boxID.
Common situations: The encrypted notebook's box ID appears as a path prefix (e.g. `data/<encrypted-box-id>/...`). A user attempts to bypass the (locked) block tools by editing `.sy` files directly through the file tool.
Related errors
- encrypted notebook is locked, please unlock it first
- CLI does not support encrypted notebook [%s]
- CLI does not support files in encrypted notebooks
- encrypted notebook is locked, please unlock it first
- path escapes workspace: %s
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/0406d11f9797372a.
Report an issue: GitHub.