playwright_rs/error.rs
1// Error types for playwright-core
2
3use thiserror::Error;
4
5/// Result type alias for playwright-core operations
6pub type Result<T> = std::result::Result<T, Error>;
7
8/// Errors that can occur when using playwright-core
9#[derive(Debug, Error)]
10#[non_exhaustive]
11pub enum Error {
12 /// No Playwright driver could be located
13 ///
14 /// The crate's build script normally assembles the driver at compile
15 /// time. The message says what became of that driver for this build:
16 /// the download failed (cargo hides a dependency's build warnings, so
17 /// this is the first place a consumer hears of it), it was skipped with
18 /// `PLAYWRIGHT_SKIP_DRIVER_DOWNLOAD`, or it has since gone missing. It
19 /// then names the ways to provide one: rebuilding, `playwright-rs
20 /// install`, or `PLAYWRIGHT_DRIVER_PATH`.
21 #[error("{}", crate::server::driver::BundledDriver::for_this_build().server_not_found_message())]
22 ServerNotFound,
23
24 /// Failed to launch the Playwright server process
25 ///
26 /// The Playwright server process could not be started.
27 /// Common causes: Node.js not installed, insufficient permissions, or port already in use.
28 /// Details: {0}
29 #[error("Failed to launch Playwright server: {0}. Check that Node.js is installed.")]
30 LaunchFailed(String),
31
32 /// A driver override environment variable is set but does not point at
33 /// a usable driver
34 ///
35 /// `PLAYWRIGHT_DRIVER_PATH`, `PLAYWRIGHT_NODE_EXE`, or `PLAYWRIGHT_CLI_JS`
36 /// named a path that does not exist. The message names the variable.
37 /// Unset the variable to use the bundled driver, or point it at the right
38 /// path.
39 #[error("Playwright driver override is misconfigured: {0}")]
40 DriverMisconfigured(String),
41
42 /// Server error (runtime issue with Playwright server)
43 #[error("Server error: {0}")]
44 ServerError(String),
45
46 /// Browser is not installed
47 ///
48 /// The specified browser has not been installed for the bundled driver.
49 /// To resolve this, install browsers through the crate (version-safe), or
50 /// with the one-off npx command using the exact bundled driver version.
51 #[error(
52 "Browser '{browser_name}' is not installed.\n\n\
53 {message}\n\n\
54 To install {browser_name} from your project (version-safe, rides Cargo.lock):\n \
55 playwright_rs::install_browsers(Some(&[\"{browser_name}\"])).await\n \
56 (see the crate's install-browsers example)\n\n\
57 Or as a one-off shell command:\n \
58 npx playwright@{playwright_version} install {browser_name}\n \
59 (the version must match the bundled driver; avoid hardcoding it in CI)\n\n\
60 See: https://playwright.dev/docs/browsers"
61 )]
62 BrowserNotInstalled {
63 browser_name: String,
64 message: String,
65 playwright_version: String,
66 },
67
68 /// Failed to establish connection with the server
69 #[error("Failed to connect to Playwright server: {0}")]
70 ConnectionFailed(String),
71
72 /// Transport-level error (stdio communication)
73 #[error("Transport error: {0}")]
74 TransportError(String),
75
76 /// Protocol-level error (JSON-RPC)
77 #[error("Protocol error: {0}")]
78 ProtocolError(String),
79
80 /// I/O error
81 #[error("I/O error: {0}")]
82 Io(#[from] std::io::Error),
83
84 /// JSON serialization/deserialization error
85 #[error("JSON error: {0}")]
86 Json(#[from] serde_json::Error),
87
88 /// Timeout waiting for operation
89 ///
90 /// Contains context about what operation timed out and the timeout duration.
91 /// Common causes include slow network, server not responding, or element not becoming actionable.
92 /// Consider increasing the timeout or checking if the target is accessible.
93 #[error("Timeout: {0}")]
94 Timeout(String),
95
96 /// Navigation timeout
97 ///
98 /// Occurs when page navigation exceeds the specified timeout.
99 /// Includes the URL being navigated to and timeout duration.
100 #[error("Navigation timeout after {duration_ms}ms navigating to '{url}'")]
101 NavigationTimeout { url: String, duration_ms: u64 },
102
103 /// Target was closed (browser, context, or page)
104 ///
105 /// Occurs when attempting to perform an operation on a closed target.
106 /// The target must be recreated before it can be used again.
107 #[error("Target closed: Cannot perform operation on closed {target_type}. {context}")]
108 TargetClosed {
109 target_type: String,
110 context: String,
111 },
112
113 /// Unknown protocol object type
114 #[error("Unknown protocol object type: {0}")]
115 UnknownObjectType(String),
116
117 /// Channel closed unexpectedly
118 #[error("Channel closed unexpectedly")]
119 ChannelClosed,
120
121 /// Invalid argument provided to method
122 #[error("Invalid argument: {0}")]
123 InvalidArgument(String),
124
125 /// Element not found by selector
126 ///
127 /// Includes the selector that was used to locate the element.
128 /// This error typically occurs when waiting for an element times out.
129 #[error("Element not found: selector '{0}'")]
130 ElementNotFound(String),
131
132 /// Assertion timeout (expect API)
133 #[error("Assertion timeout: {0}")]
134 AssertionTimeout(String),
135
136 /// Assertion failed (expect API, server returned mismatch without timeout)
137 #[error("Assertion failed: {0}")]
138 AssertionFailed(String),
139 /// Object not found in registry (may have been closed/disposed)
140 #[error("Object not found (may have been closed): {0}")]
141 ObjectNotFound(String),
142
143 /// Type mismatch when downcasting a protocol object
144 ///
145 /// Occurs when a protocol object's concrete type does not match the expected type.
146 /// This typically indicates a Playwright protocol version mismatch or a bug in the
147 /// object factory.
148 #[error("Type mismatch for object '{guid}': expected {expected}, got {actual}")]
149 TypeMismatch {
150 guid: String,
151 expected: String,
152 actual: String,
153 },
154
155 /// Invalid path provided
156 #[error("Invalid path: {0}")]
157 InvalidPath(String),
158
159 /// Error with additional context
160 #[error("{0}: {1}")]
161 Context(String, #[source] Box<Error>),
162}
163
164impl Error {
165 /// Adds context to the error
166 pub fn context(self, msg: impl Into<String>) -> Self {
167 Error::Context(msg.into(), Box::new(self))
168 }
169}