Implementation:Webdriverio Webdriverio Error Handling Pattern
Template:Implementation Metadata
Overview
The Error Handling Pattern is an interface specification and set of concrete classes for handling WebDriver errors in standalone browser automation scripts. WebdriverIO maps W3C WebDriver protocol errors to JavaScript Error objects, providing structured error information for programmatic handling.
Description
WebdriverIO maps W3C WebDriver protocol errors to JavaScript Error objects through a class hierarchy. The error handling system operates at two levels:
Error Classes
WebDriverError(abstract base class): Provides thecomputeErrorMessage()method that enriches error messages with the executing command name, HTTP method, and arguments. All WebDriver errors extend this class.
WebDriverRequestError: Represents errors at the HTTP transport level -- connection failures, timeouts, and network errors. Contains the original error, the request URL, and request options. Handles special cases likefetch failed(connection refused) andUND_ERR_CONNECT_TIMEOUT(connection timeout).
WebDriverResponseError: Represents errors returned in the WebDriver protocol response body. Parses the W3C error format ({ value: { error, message, stacktrace } }) and handles browser-specific variations (Chrome, Firefox, Safari). Maps the protocolerrorfield to the JavaScriptError.nameproperty for programmatic matching.
Element Lookup Error Pattern
The findElement() utility in WebdriverIO follows a distinctive pattern: when an element is not found, it returns an Error instance rather than throwing. This enables the element wrapper to create a "not found" element object that defers the error until the user actually tries to interact with it. This is different from the standard protocol behavior (which would throw immediately) and enables patterns like:
const elem = await browser.$('#might-not-exist')
// No error thrown yet -- elem is a valid object
const exists = await elem.isExisting()
// Now you can check without try/catch
Error Message Enrichment
All WebDriver errors are enriched with contextual information via computeErrorMessage():
- The command name being executed (extracted from the URL path)
- The HTTP method used
- The command arguments (with sensitive data masked)
This produces error messages like:
WebDriverError: no such element when running "findElement" with method "POST" and args "{"using":"css selector","value":"#missing"}"
Source Locations
| Component | File | Lines |
|---|---|---|
WebDriverError (abstract base) |
packages/webdriver/src/request/error.ts |
L5-50 |
WebDriverRequestError |
packages/webdriver/src/request/error.ts |
L52-90 |
WebDriverResponseError |
packages/webdriver/src/request/error.ts |
L92-154 |
findElement() error handling |
packages/webdriverio/src/utils/index.ts |
L463-558 |
| Session abort mechanism | packages/webdriver/src/command.ts |
L221-274 |
Repository: https://github.com/webdriverio/webdriverio
Error Types
| Class | Parent | Description |
|---|---|---|
WebDriverError |
Error |
Abstract base class for all WebDriver errors. Provides computeErrorMessage().
|
WebDriverRequestError |
WebDriverError |
Transport-level errors (connection refused, timeout, network failure). Contains statusCode, body, and code properties.
|
WebDriverResponseError |
WebDriverError |
Protocol-level errors from the WebDriver response body. The name property is set to the W3C error code (e.g., "no such element", "stale element reference").
|
Pattern Interface
The standard error handling interface for standalone WebdriverIO scripts:
// Error class interface
abstract class WebDriverError extends Error {
abstract url: URL
abstract opts: RequestInit
computeErrorMessage(): string
}
class WebDriverRequestError extends WebDriverError {
url: URL
opts: RequestInit
statusCode?: number
body?: unknown
code?: string
constructor(err: Error, url: URL, opts: RequestInit)
}
class WebDriverResponseError extends WebDriverError {
url: URL
opts: RequestInit
constructor(response: unknown, url: URL, opts: RequestInit)
}
Usage Example
Basic try/catch/finally pattern:
import { remote } from 'webdriverio'
const browser = await remote({
capabilities: { browserName: 'chrome' }
})
try {
await browser.url('https://example.com')
// Element interaction with error handling
const submitBtn = await browser.$('#submit-button')
await submitBtn.waitForDisplayed({ timeout: 5000 })
await submitBtn.click()
} catch (err) {
// Classify the error by its name (W3C error code)
if (err.name === 'no such element') {
console.error('Element not found:', err.message)
} else if (err.name === 'stale element reference') {
console.error('Element became stale:', err.message)
} else if (err.name === 'timeout') {
console.error('Operation timed out:', err.message)
} else if (err.name === 'invalid session id') {
console.error('Session was lost:', err.message)
} else {
console.error('Unexpected error:', err.message)
}
// Capture diagnostic information
try {
await browser.saveScreenshot('./error-screenshot.png')
} catch (_) {
// Screenshot may fail if session is invalid
}
} finally {
// Always clean up the session
try {
await browser.deleteSession()
} catch (_) {
// Session may already be invalid
}
}
Graceful element existence check (leveraging the non-throwing findElement pattern):
const browser = await remote({
capabilities: { browserName: 'chrome' }
})
try {
await browser.url('https://example.com')
// $ does not throw when element is not found
// Instead it returns an element object that knows it was not found
const elem = await browser.$('#optional-element')
if (await elem.isExisting()) {
await elem.click()
} else {
console.log('Optional element not present, skipping')
}
} finally {
await browser.deleteSession()
}
Retry pattern for stale elements:
async function clickWithRetry(browser, selector, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const elem = await browser.$(selector)
await elem.click()
return // Success
} catch (err) {
if (err.name === 'stale element reference' && attempt < maxRetries - 1) {
console.log(`Stale element, retrying (${attempt + 1}/${maxRetries})`)
continue
}
throw err // Re-throw if not recoverable
}
}
}
Internal Flow
Request Error Path
- A protocol command is issued via the command factory (
command.ts). - The request is sent via the
Requestclass. - If the HTTP request itself fails (network error, timeout):
- A
WebDriverRequestErroris constructed with the original error, URL, and request options. - The error message is enriched with command context via
computeErrorMessage(). - The error is thrown to the caller.
- A
Response Error Path
- The HTTP response is received successfully but contains an error in the body.
- The response body is parsed to extract the W3C error fields (
error,message,stacktrace). - A
WebDriverResponseErroris constructed:- The
nameproperty is set to the W3C error code (e.g.,"no such element"). - Browser-specific error formats (Chrome, Firefox, Safari) are normalized.
- Invalid selector errors receive improved error messages with the actual selector and strategy.
- The
- The error message is enriched via
computeErrorMessage(). - The error is thrown to the caller.
Element Not Found Path
findElement()is called with a selector.- The protocol command returns no matching element.
- Instead of throwing, an
Errorinstance is created:new Error('Couldn't find element with selector "..."'). - This Error is returned (not thrown) and passed to
getElement(). getElement()wraps it in a "not found" element object.- The user can check
isExisting()without triggering an exception. - If the user calls an action method (e.g.,
click()), the deferred error is thrown at that point.
Related Pages
- implements Principle:Webdriverio_Webdriverio_WebDriver_Error_Handling - The WebDriver Error Handling principle that this pattern implements.
- Heuristic:Webdriverio_Webdriverio_Stale_Element_Auto_Refetch
- Heuristic:Webdriverio_Webdriverio_Exponential_Backoff_Retry