Skip to main content

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}