1use std::path::PathBuf;
4
5use crate::PluginId;
6
7#[derive(Debug, thiserror::Error)]
9pub enum PluginError {
10 #[error("plugin not found: {0}")]
12 NotFound(PluginId),
13
14 #[error("plugin already registered: {0}")]
16 AlreadyRegistered(PluginId),
17
18 #[error("manifest parse error in {path}: {message}")]
20 ManifestParseError {
21 path: PathBuf,
23 message: String,
25 },
26
27 #[error("plugin load failed: {plugin_id} - {message}")]
29 LoadFailed {
30 plugin_id: PluginId,
32 message: String,
34 },
35
36 #[error("plugin execution failed: {0}")]
38 ExecutionFailed(String),
39
40 #[error("tool not found: {0}")]
42 ToolNotFound(String),
43
44 #[error("invalid plugin id: {0}")]
46 InvalidId(String),
47
48 #[error("storage error: {0}")]
50 Storage(String),
51
52 #[error("MCP server failed for plugin {plugin_id}: {message}")]
54 McpServerFailed {
55 plugin_id: PluginId,
57 message: String,
59 },
60
61 #[error("MCP client required for MCP plugin entry point")]
63 McpClientRequired,
64
65 #[error("unsupported entry point type: {0}")]
67 UnsupportedEntryPoint(String),
68
69 #[error("sandbox error: {0}")]
71 SandboxError(String),
72
73 #[error("I/O error: {0}")]
75 Io(#[from] std::io::Error),
76
77 #[error("WASM error: {0}")]
79 WasmError(String),
80
81 #[error("registry error: {message}")]
83 RegistryError {
84 message: String,
86 },
87
88 #[error("integrity mismatch for {package}: expected {expected}")]
90 IntegrityError {
91 package: String,
93 expected: String,
95 },
96
97 #[error("extraction error: {message}")]
99 ExtractionError {
100 message: String,
102 },
103
104 #[error("unsafe archive entry type '{entry_type}' at {path}")]
106 UnsafeEntryType {
107 entry_type: String,
109 path: String,
111 },
112
113 #[error("path traversal detected: {path}")]
115 PathTraversal {
116 path: String,
118 },
119
120 #[error("package too large: {size} bytes (limit: {limit} bytes)")]
122 PackageTooLarge {
123 size: u64,
125 limit: u64,
127 },
128
129 #[error("not an OpenClaw plugin: missing openclaw.plugin.json")]
131 NotOpenClawPlugin,
132
133 #[error("invalid package name '{name}': {reason}")]
135 InvalidPackageName {
136 name: String,
138 reason: String,
140 },
141
142 #[error("SSRF blocked: tarball URL {url} does not match registry")]
144 SsrfBlocked {
145 url: String,
147 },
148
149 #[error("security denied: {0}")]
151 SecurityDenied(String),
152
153 #[error("hash mismatch: expected {expected}, got {actual}")]
155 HashMismatch {
156 expected: String,
158 actual: String,
160 },
161
162 #[error("lockfile error at {path}: {message}")]
164 LockfileError {
165 path: PathBuf,
167 message: String,
169 },
170}
171
172impl From<astrid_storage::StorageError> for PluginError {
173 fn from(e: astrid_storage::StorageError) -> Self {
174 Self::Storage(e.to_string())
175 }
176}
177
178pub type PluginResult<T> = Result<T, PluginError>;