Documentation Index
Fetch the complete documentation index at: https://mintlify.com/browserbase/stagehand/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Robust error handling is essential for production Stagehand applications. This guide covers error types, recovery strategies, and best practices.
Error Types
Stagehand provides specific error classes for different failure modes.
Timeout Errors
import {
ActTimeoutError,
ExtractTimeoutError,
ObserveTimeoutError
} from "@browserbasehq/stagehand";
try {
await stagehand.act("click submit button", { timeout: 5000 });
} catch (error) {
if (error instanceof ActTimeoutError) {
console.log("Action timed out after 5 seconds");
// Retry with longer timeout or different strategy
}
}
Timeout errors:
ActTimeoutError - act() operation timed out
ExtractTimeoutError - extract() operation timed out
ObserveTimeoutError - observe() operation timed out
TimeoutError - Base timeout error class
Element Not Found Errors
import { StagehandElementNotFoundError } from "@browserbasehq/stagehand";
try {
await stagehand.act("click the non-existent button");
} catch (error) {
if (error instanceof StagehandElementNotFoundError) {
console.log("Element not found:", error.message);
// Element doesn't exist on page
}
}
Initialization Errors
import {
StagehandNotInitializedError,
StagehandInitError
} from "@browserbasehq/stagehand";
try {
const stagehand = new Stagehand({ env: "LOCAL" });
// ❌ Forgot to call init()
await stagehand.act("click button");
} catch (error) {
if (error instanceof StagehandNotInitializedError) {
console.log("Stagehand not initialized. Call await stagehand.init() first.");
}
}
Configuration Errors
import {
MissingEnvironmentVariableError,
UnsupportedModelError,
MissingLLMConfigurationError
} from "@browserbasehq/stagehand";
try {
const stagehand = new Stagehand({
env: "BROWSERBASE",
// Missing apiKey and projectId
});
await stagehand.init();
} catch (error) {
if (error instanceof MissingEnvironmentVariableError) {
console.log("Missing required environment variable:", error.message);
}
}
LLM Response Errors
import { LLMResponseError } from "@browserbasehq/stagehand";
try {
await stagehand.extract("get product data");
} catch (error) {
if (error instanceof LLMResponseError) {
console.log("LLM returned invalid response:", error.message);
// Retry or fall back to different approach
}
}
Validation Errors
import { ZodSchemaValidationError } from "@browserbasehq/stagehand";
import { z } from "zod";
const schema = z.object({
price: z.number(),
title: z.string(),
});
try {
const result = await stagehand.extract(
"extract product info",
{ schema }
);
} catch (error) {
if (error instanceof ZodSchemaValidationError) {
console.log("Extraction didn't match schema:", error.issues);
console.log("Received:", error.received);
}
}
Error Handling Patterns
Basic Try-Catch
try {
await stagehand.act("click submit");
} catch (error) {
console.error("Action failed:", error);
throw error; // Re-throw or handle
}
Retry with Exponential Backoff
async function actWithRetry(
stagehand: Stagehand,
instruction: string,
maxRetries = 3
) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
return await stagehand.act(instruction);
} catch (error) {
lastError = error;
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
console.log(`Retry ${i + 1}/${maxRetries} after ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
// Usage
try {
await actWithRetry(stagehand, "click the submit button");
} catch (error) {
console.error("Failed after retries:", error);
}
Graceful Degradation
let productData;
try {
// Try AI-powered extraction
const result = await stagehand.extract("extract product details");
productData = result.extraction;
} catch (error) {
console.warn("AI extraction failed, falling back to selectors");
// Fall back to direct selectors
const page = stagehand.context.pages()[0];
productData = {
title: await page.textContent(".product-title"),
price: await page.textContent(".product-price"),
};
}
Timeout Strategy
const SHORT_TIMEOUT = 5_000;
const MEDIUM_TIMEOUT = 15_000;
const LONG_TIMEOUT = 30_000;
try {
// Try with short timeout first
await stagehand.act("click button", { timeout: SHORT_TIMEOUT });
} catch (error) {
if (error instanceof ActTimeoutError) {
console.log("Retrying with longer timeout...");
// Retry with longer timeout
await stagehand.act("click button", { timeout: MEDIUM_TIMEOUT });
} else {
throw error;
}
}
Handling Agent Errors
Agent Abort
import { AgentAbortError } from "@browserbasehq/stagehand";
const agent = stagehand.agent();
try {
const result = await agent.execute({
instruction: "complete the checkout process",
maxSteps: 20,
});
} catch (error) {
if (error instanceof AgentAbortError) {
console.log("Agent aborted:", error.reason);
// Agent couldn't complete the task
}
}
Agent Max Steps Reached
const agent = stagehand.agent();
const result = await agent.execute({
instruction: "find and click the submit button",
maxSteps: 5,
});
if (!result.success) {
console.log("Agent couldn't complete task in 5 steps");
console.log("Steps taken:", result.actions?.length);
// Retry with more steps
const retryResult = await agent.execute({
instruction: "find and click the submit button",
maxSteps: 10,
});
}
Connection Errors
CDP Connection Closed
import { CdpConnectionClosedError } from "@browserbasehq/stagehand";
try {
await stagehand.act("click button");
} catch (error) {
if (error instanceof CdpConnectionClosedError) {
console.log("Browser connection closed:", error.message);
// Browser crashed or session ended
// Need to create new Stagehand instance
}
}
Browserbase Connection
import {
ConnectionTimeoutError,
BrowserbaseSessionNotFoundError
} from "@browserbasehq/stagehand";
try {
const stagehand = new Stagehand({
env: "BROWSERBASE",
apiKey: process.env.BROWSERBASE_API_KEY,
projectId: process.env.BROWSERBASE_PROJECT_ID,
});
await stagehand.init();
} catch (error) {
if (error instanceof ConnectionTimeoutError) {
console.log("Connection timed out:", error.message);
} else if (error instanceof BrowserbaseSessionNotFoundError) {
console.log("Session not found");
}
}
Cleanup and Resource Management
Always Close
const stagehand = new Stagehand({ env: "LOCAL" });
try {
await stagehand.init();
await stagehand.act("perform actions");
} catch (error) {
console.error("Error during execution:", error);
throw error;
} finally {
// Always clean up resources
await stagehand.close();
}
Detect Closed State
import { StagehandClosedError } from "@browserbasehq/stagehand";
try {
await stagehand.act("click button");
} catch (error) {
if (error instanceof StagehandClosedError) {
console.log("Stagehand session was closed");
// Cannot use this instance anymore
}
}
Logging Errors
Custom Logger
const stagehand = new Stagehand({
env: "LOCAL",
verbose: 2,
logger: (line) => {
if (line.level === 0) {
// Error level
console.error(`[${line.category}] ${line.message}`, line.auxiliary);
}
},
});
Persist Error Logs
import fs from "fs";
const stagehand = new Stagehand({
env: "LOCAL",
verbose: 2,
logInferenceToFile: true, // Saves detailed logs to file
});
try {
await stagehand.init();
await stagehand.act("click button");
} catch (error) {
// Error logs automatically saved to file
console.error("Check logs directory for details");
throw error;
}
Production Error Handling
Comprehensive Error Handler
import {
ActTimeoutError,
StagehandElementNotFoundError,
LLMResponseError,
StagehandError,
} from "@browserbasehq/stagehand";
async function performAction(
stagehand: Stagehand,
instruction: string
) {
try {
return await stagehand.act(instruction, { timeout: 15_000 });
} catch (error) {
// Handle specific errors
if (error instanceof ActTimeoutError) {
console.error("Action timed out");
// Retry or notify user
throw new Error("Operation timed out. Please try again.");
} else if (error instanceof StagehandElementNotFoundError) {
console.error("Element not found:", error.message);
// Page structure may have changed
throw new Error("Could not find the required element.");
} else if (error instanceof LLMResponseError) {
console.error("LLM error:", error.message);
// Model issue, retry with different model
throw new Error("AI service error. Please try again.");
} else if (error instanceof StagehandError) {
console.error("Stagehand error:", error.message);
throw error;
} else {
// Unknown error
console.error("Unexpected error:", error);
throw new Error("An unexpected error occurred.");
}
}
}
Error Monitoring
import * as Sentry from "@sentry/node";
try {
await stagehand.act("click button");
} catch (error) {
// Report to error tracking service
Sentry.captureException(error, {
tags: {
operation: "act",
instruction: "click button",
},
extra: {
page_url: stagehand.context.pages()[0]?.url(),
},
});
throw error;
}
Testing Error Scenarios
import { test, expect } from "@playwright/test";
test("handles timeout gracefully", async () => {
const stagehand = new Stagehand({ env: "LOCAL" });
await stagehand.init();
try {
// This should timeout
await stagehand.act("click non-existent button", { timeout: 1000 });
// Should not reach here
expect(true).toBe(false);
} catch (error) {
expect(error).toBeInstanceOf(ActTimeoutError);
} finally {
await stagehand.close();
}
});
test("retries on failure", async () => {
const stagehand = new Stagehand({ env: "LOCAL" });
await stagehand.init();
let attempts = 0;
const maxAttempts = 3;
let result;
for (let i = 0; i < maxAttempts; i++) {
try {
attempts++;
result = await stagehand.act("click button");
break; // Success
} catch (error) {
if (i === maxAttempts - 1) throw error;
await new Promise(r => setTimeout(r, 1000));
}
}
expect(attempts).toBeLessThanOrEqual(maxAttempts);
expect(result).toBeDefined();
await stagehand.close();
});
Error Prevention
Validate Before Acting
const page = stagehand.context.pages()[0];
// Check if element exists before acting
const buttonExists = await page.locator("button[type='submit']").count() > 0;
if (buttonExists) {
await stagehand.act("click the submit button");
} else {
console.log("Submit button not found on page");
// Handle missing element
}
Wait for Page State
const page = stagehand.context.pages()[0];
await page.goto("https://example.com");
// Wait for content to load
await page.waitForLoadState("networkidle");
// Now safe to act
await stagehand.act("click the first product");
Use Appropriate Timeouts
// Short timeout for simple actions
await stagehand.act("click button", { timeout: 5_000 });
// Longer timeout for complex operations
await stagehand.extract("extract all product data", { timeout: 30_000 });
// Very long timeout for agent workflows
const agent = stagehand.agent();
await agent.execute({
instruction: "complete checkout",
maxSteps: 20,
// Individual steps have their own timeouts
});
Error Reference
Common errors from sdkErrors.ts:1-438:
| Error Class | When It Occurs | Recovery Strategy |
|---|
ActTimeoutError | act() times out | Increase timeout, retry |
ExtractTimeoutError | extract() times out | Increase timeout, simplify extraction |
ObserveTimeoutError | observe() times out | Increase timeout, be more specific |
StagehandElementNotFoundError | Element doesn’t exist | Check page structure, wait for load |
StagehandNotInitializedError | Forgot to call init() | Call await stagehand.init() |
MissingLLMConfigurationError | No LLM API key | Set API key or provide LLM client |
ZodSchemaValidationError | Extraction doesn’t match schema | Adjust schema or prompt |
AgentAbortError | Agent can’t complete task | Adjust instruction or increase steps |
CdpConnectionClosedError | Browser connection lost | Create new Stagehand instance |
StagehandClosedError | Used after close() | Don’t use after closing |
Related