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

  1. Use the dedicated block/content tools (which respect encryption and unlock state) instead of raw file access for encrypted notebooks.
  2. Unlock the notebook and operate through the documented block API rather than the file tool.
  3. 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

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


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