---
title: "Errors"
sidebarTitle: "Errors"
description: "Custom error classes and error handling"
icon: "triangle-exclamation"
mode: "wide"
---

## Overview

TestDriver provides custom error classes with rich debugging information. These are exported from the SDK and can be used for `instanceof` checks in your tests.

```javascript
import TestDriver, { ElementNotFoundError, AIError } from 'testdriverai';
```

## ElementNotFoundError

Thrown when `find()` cannot locate an element on screen, or when calling `click()`/`hover()` on an unfound element.

```javascript
try {
  await testdriver.find('nonexistent button').click();
} catch (error) {
  if (error instanceof ElementNotFoundError) {
    console.log(error.description);       // "nonexistent button"
    console.log(error.screenshotPath);    // path to debug screenshot
    console.log(error.pixelDiffPath);     // path to pixel diff image
  }
}
```

### Properties

<ParamField path="name" type="string">
  Always `"ElementNotFoundError"`.
</ParamField>

<ParamField path="message" type="string">
  Enhanced message with a debug block containing element description, cache status, similarity scores, and AI response details.
</ParamField>

<ParamField path="description" type="string">
  The original element description passed to `find()`.
</ParamField>

<ParamField path="screenshotPath" type="string | null">
  Absolute path to a debug screenshot saved at the time of failure. Written to `<os.tmpdir>/testdriver-debug/screenshot-<timestamp>.png`.
</ParamField>

<ParamField path="pixelDiffPath" type="string | null">
  Absolute path to a pixel diff image showing the comparison between the cached and current screenshots. Written to `<os.tmpdir>/testdriver-debug/pixel-diff-error-<timestamp>.png`. Only present when cache was involved.
</ParamField>

<ParamField path="cachedImagePath" type="string | null">
  URL to the cached image that was compared against.
</ParamField>

<ParamField path="aiResponse" type="object | null">
  Sanitized AI response object. Large binary fields (`croppedImage`, `screenshot`, `pixelDiffImage`) are removed. Contains cache metadata like `similarity`, `cacheHit`, `cacheStrategy`, `cacheDiffPercent`, and `threshold`.
</ParamField>

<ParamField path="timestamp" type="string">
  ISO 8601 timestamp of when the error was created.
</ParamField>

### Enhanced Message

The error message is automatically enhanced with debugging information:

```
Element not found: "submit button"

=== Element Debug Info ===
Element: submit button
Cache Hit: false
Similarity: 0.23
Cache Strategy: pixel-diff
Threshold: 0.05
AI Response Element: null
```

### Stack Trace Cleaning

Stack traces are automatically cleaned to remove internal SDK frames (`Element.*`, `sdk.js` internals), showing only your test code for easier debugging.

## AIError

Thrown when `act()` exhausts all retry attempts.

```javascript
try {
  await testdriver.act('perform complex workflow', { tries: 3 });
} catch (error) {
  if (error instanceof AIError) {
    console.log(error.task);       // "perform complex workflow"
    console.log(error.tries);     // 3
    console.log(error.duration);  // 15234 (ms)
    console.log(error.cause);     // underlying Error
  }
}
```

### Properties

<ParamField path="name" type="string">
  Always `"AIError"`.
</ParamField>

<ParamField path="message" type="string">
  Enhanced message with execution details block.
</ParamField>

<ParamField path="task" type="string">
  The task description passed to `act()`.
</ParamField>

<ParamField path="tries" type="number">
  Number of attempts that were made.
</ParamField>

<ParamField path="maxTries" type="number">
  Maximum number of attempts configured.
</ParamField>

<ParamField path="duration" type="number">
  Total execution time in milliseconds.
</ParamField>

<ParamField path="cause" type="Error | undefined">
  The underlying error that caused the final failure.
</ParamField>

<ParamField path="timestamp" type="string">
  ISO 8601 timestamp of when the error was created.
</ParamField>

### Enhanced Message

```
AI failed: Element not found after 3 attempts

=== AI Execution Details ===
Task: perform complex workflow
Tries: 3 / 3
Duration: 15234ms
Cause: ElementNotFoundError: Element not found: "submit button"
```

## Internal Error Classes

These errors are used internally by the agent and are not exported, but may appear as the `cause` of an `AIError`:

### MatchError

Thrown when element matching fails (text, image, or assertion).

| Property | Type | Description |
|---|---|---|
| `fatal` | `boolean` | If `true`, cannot be healed. Default: `false` |
| `attachScreenshot` | `boolean` | Always `true` — a screenshot is attached to the error |

### CommandError

Thrown for invalid arguments or unsupported operations.

| Property | Type | Description |
|---|---|---|
| `fatal` | `boolean` | Always `true` |
| `attachScreenshot` | `boolean` | Always `false` |

## Soft Assert Mode

Inside `act()`, assertions run in **soft assert mode**. When an assertion fails, it returns the failure result instead of throwing, allowing the AI to process the failure and adjust its approach.

```javascript
// Inside act(), assertion failures don't throw
await testdriver.act('verify the dashboard shows correct data', {
  tries: 3,
});
// The AI can see assertion results and self-correct
```

This is automatic — you don't need to configure it. Regular `assert()` calls outside of `act()` will throw normally on failure.

## Error Handling Patterns

### Catching Specific Errors

```javascript
import TestDriver, { ElementNotFoundError, AIError } from 'testdriverai';

try {
  await testdriver.find('submit button').click();
} catch (error) {
  if (error instanceof ElementNotFoundError) {
    // Element wasn't found — check screenshot for debugging
    console.log('Debug screenshot:', error.screenshotPath);
  } else if (error instanceof AIError) {
    // AI exhausted retries
    console.log(`Failed after ${error.tries} tries in ${error.duration}ms`);
  } else {
    throw error; // Unexpected error
  }
}
```

### Using Debug Screenshots

```javascript
try {
  const el = await testdriver.find('checkout button');
  await el.click();
} catch (error) {
  if (error instanceof ElementNotFoundError) {
    // Screenshot of what the screen looked like
    console.log('Screen:', error.screenshotPath);
    // Pixel diff showing cache comparison
    console.log('Diff:', error.pixelDiffPath);
    // Full AI response metadata
    console.log('AI:', JSON.stringify(error.aiResponse, null, 2));
  }
}
```

## Types

```typescript
class ElementNotFoundError extends Error {
  name: 'ElementNotFoundError';
  description: string;
  screenshotPath: string | null;
  pixelDiffPath: string | null;
  cachedImagePath: string | null;
  aiResponse: Record<string, any> | null;
  timestamp: string;
}

class AIError extends Error {
  name: 'AIError';
  task: string;
  tries: number;
  maxTries: number;
  duration: number;
  cause?: Error;
  timestamp: string;
}
```
