Smoketest

Stagehand Error Handling in Production

Stagehand failures arrive as schema errors, stale actions, early agent exits, startup timeouts, and deployment faults, but the docs do not put them in one error-handling model. This guide classifies each failure, decides what deserves a retry, and shows how to preserve the prompt and action trail for the postmortem.

10 min read

Your Stagehand test passed locally, then production returned StagehandDefaultError: Invalid response schema, stopped halfway through a task, or timed out before the first page appeared. You searched the message because the exception alone does not tell you whether to retry, fall back, or stop the run.

Stagehand error handling needs a taxonomy, not one catch-all retry. Retry intermittent model responses and browser startup once, re-observe stale actions, give multi-step work to an agent, and fail fast on build or configuration faults. Record the prompt, Stagehand history, result, and final error together so every retry has evidence.

Stagehand v3's documentation index has pages for logging, history, deployments, and agent fallbacks, but no single error-handling page (Stagehand documentation index). The pieces are there. Production code has to assemble them into a policy.

This guide targets Stagehand v3 as of mid-2026. V3 launched on October 29, 2025, talks to the browser through Chrome DevTools Protocol, and removed Playwright as an internal dependency (Stagehand v3 launch). That matters because several useful failure reports came from v1 or v2. They document real symptoms, not proof that the same root cause remains in v3.

Start with five failure classes

Do not route every error through the same retry loop. Classify it by the layer that failed.

Error classTypical signalRetry policyFallback
Model responseStagehandDefaultError with Invalid response schemaRetry once with the same stateNarrow the instruction or stop with evidence
Action resolutionAn observed action cannot resolve its element and times outDo not replay the same actionRe-observe, validate, then act on the new action
Agent completionAgent returns before the business goal is completeDo not blindly rerun the whole taskIncrease the documented step budget or split the task
Browser startupwaitForFirstTopLevelPage times outRetry initialization once in a fresh sessionMove browser execution to the documented hosted setup
Build or runtimeMissing bundled module or browser helperNever retry the requestFix the artifact or runtime before accepting traffic

The retry limit is an operating recommendation, not a Stagehand guarantee. One retry absorbs a transient response or cold start. Repeating the same failed input several times spends tokens and hides a deterministic defect.

Invalid response schema

Issue #676 records the exact v1-era wrapper and cause:

Text
StagehandDefaultError:
Full error:
Invalid response schema

The reporter saw it intermittently during act and typed extract calls. The issue is now closed as not planned, with no maintainer workaround in the thread (issue #676). Do not turn that status into “fixed.” It only tells us the repository will not pursue that report as filed.

What it means operationally is narrower: Stagehand expected structured model output and did not receive data matching the expected shape. It is retry-worthy once because the report itself says the failure was intermittent. If the second attempt returns the same Stagehand invalid response schema error, stop. Check the instruction, extraction schema, model choice, and captured result rather than adding more retries.

Keep schema failures separate from your application rejecting valid extracted data. The first is a model-to-Stagehand contract failure. The second is your domain validation doing its job. They should produce different alert labels even if both end with a failed run.

Unresolvable observed actions

An observation can be syntactically valid and still point nowhere useful. In issue #657, observe returned selector: 'xpath=/div/a[1]', then acting on it produced success: false with this message:

Text
Failed to perform act: locator.evaluate: Timeout 30000ms exceeded.
Call log:
 - waiting for locator('xpath=/div/a[1]').first()

The issue is closed, but the thread contains no maintainer diagnosis or workaround (issue #657). Current v3 guidance supplies the safer pattern: call observe, inspect the candidate action, and pass that action to act (Stagehand act guide).

The important retry rule is to re-observe. Replaying the same unresolved XPath asks the browser to wait for the same missing target again. A new observation lets Stagehand read the current page state. Validate that the returned description and method match the intended operation before executing it. If there is no acceptable action, stop instead of letting the model choose a nearby element.

This class also includes page-state drift. A menu may have closed, a route may have changed, or an iframe may not be ready. Record the observed action and the page URL with the error. The timeout is the symptom. The stale or ambiguous target is the useful postmortem fact.

Agents that stop early

An agent can return without throwing and still fail the business goal. Discussion #679 describes an agent that gave up on “Find and open the most recent blog post.” The accepted answer recommends a more specific goal, smaller steps, and code for critical actions. That 2025 discussion used the earlier Playwright-based architecture, so it is evidence of the failure mode, not current v3 API guidance.

The current agent guide is explicit: the default maxSteps is 20, complex work can use a higher value, and very complex tasks should be split into sequential executions with success checks (Stagehand agent guide). Treat result.success as data you must inspect. A resolved promise is not a passing test.

Retry only after changing the plan. Increase maxSteps when the action history shows useful progress ending at the limit. Split the instruction when one prompt contains several independently verifiable goals. For a known one-step operation, use act first and reserve the agent for the unexpected multi-step route. That is also Stagehand's documented fallback design (Agent Fallbacks).

Browser startup timeouts

The Stagehand timeout in issue #1287 is specific:

Text
TimeoutError: waitForFirstTopLevelPage (no top-level Page) timed out after 5000ms

The report used Stagehand 3.0.3 on a slower local computer. The issue is closed, with no workaround or configurable startup-timeout option documented in the thread (issue #1287). Do not present domSettleTimeout as the fix. That documented option controls how long Stagehand waits for the DOM to stabilize after a page exists, which is a different phase (browser configuration).

Retry a fresh initialization once. If it fails again, classify the run as browser startup, not action timeout. Track startup duration separately from navigation and inference time. For serverless production, follow the current deployment path: use a Node.js function with env: "BROWSERBASE", let Browserbase provide the browser, and set the function duration for the complete job (Deploying Stagehand). Function duration does not change Stagehand's internal first-page deadline, but it prevents the outer platform from ending a healthy long-running job.

Serverless build and runtime failures

Two older reports show why deployment faults belong outside the retry loop. In Stagehand 1.13.1 and 1.14.0, a Next.js API route could initialize successfully and then fail act or extract with TypeError: window.processDom is not a function; issue #623 is now closed without a maintainer workaround. In AWS Lambda, issue #845 reported Could not resolve "chromium-bidi/lib/cjs/bidiMapper/BidiMapper"; that issue remains open.

Neither error deserves a per-request retry. The deployed bundle and runtime remain unchanged between attempts. Fail the readiness check, preserve the build output, and keep the release out of traffic.

Stagehand v3 changes the dependency picture because its core no longer depends on Playwright (Stagehand v3 launch). Still, do not infer that either historical issue is fixed. The current deployment guide is the supported baseline: a Node.js Vercel Function, Stagehand v3, and a Browserbase-hosted browser (Deploying Stagehand). Reproduce that minimal deployment before adding framework bundling, queues, or scheduled invocation.

Use act first and agent second

Stagehand documents a direct fallback for a one-step action that becomes a multi-step path: try act, catch the failure, create an agent, execute with a bounded maxSteps, inspect result.success, and rethrow the original error when the fallback fails (Agent Fallbacks).

src/run-sign-in.ts

TypeScript
import { Stagehand } from "@browserbasehq/stagehand";

export async function runSignIn(stagehand: Stagehand): Promise<void> {
  const instruction = "click the 'Sign In' button";

  try {
    await stagehand.act(instruction);
  } catch (error: unknown) {
    const agent = stagehand.agent({
      model: "anthropic/claude-sonnet-4-6",
      systemPrompt: "You are a helpful assistant that can use a web browser.",
    });

    const result = await agent.execute({
      instruction: "Find and click Sign In button",
      maxSteps: 10,
    });

    if (!result.success) {
      throw error;
    }
  }
}

This is not a universal catch block. A schema mismatch may merit one retry. A missing bundle must fail immediately. The agent fallback fits one case: the intended outcome is still valid, but reaching it now requires discovery across more than one browser action.

Log the prompt and action trail

An error string without the prompt and preceding actions is rarely enough. Stagehand's history API records each Stagehand operation with method, parameters, result, and an ISO timestamp. The docs recommend saving history for critical workflows and inspecting it during failures (History Tracking).

Store one structured event per run with these fields:

  • Workflow and run identifiers
  • Stagehand major version and execution environment
  • Original instruction and fallback instruction
  • Error class, exact message, attempt number, and retry decision
  • stagehand.history, including observed actions and agent results
  • Final business result, separate from whether the SDK call threw

Redact credentials and personal data before persistence. Stagehand's variables mechanism keeps sensitive values out of model prompts, and its act guide recommends reducing verbosity when handling secrets (Stagehand act guide). Set a retention period. An unlimited archive of prompts and page-derived results becomes its own production risk.

The log should answer four questions without a rerun: What was requested? What did Stagehand attempt? Why did the policy retry or stop? Did the user-visible goal complete?

Production-hardening checklist

  • Classify failures as model response, action resolution, agent completion, browser startup, or deployment.
  • Allow at most one automatic retry for intermittent schema output or a fresh browser startup.
  • Re-observe before retrying an unresolved action. Never replay the same stale selector.
  • Check result.success after every agent execution.
  • Put a deliberate maxSteps budget on agent work and split long tests into verifiable stages.
  • Use the documented act-then-agent fallback only when a direct action may have become multi-step.
  • Run a minimal Node.js and Browserbase deployment as a release readiness check.
  • Fail fast on missing modules, injected browser helpers, credentials, and other configuration faults.
  • Save the exact error, prompts, retry decision, and Stagehand history under one run ID.
  • Redact secrets, define retention, and keep the business result separate from SDK success.
  • Alert on exhausted retries and repeated error classes, not every recovered first attempt.

Where Smoketest fits

The manual policy above works, and owning it makes sense when browser automation is core product infrastructure. It stops being a good use of engineering time when your goal is simply to know whether signup, login, or checkout still works after a deploy. Smoketest runs those tests on a platform that owns the retry and fallback layer, then returns the recording, step log, and result.

FAQ

What is StagehandDefaultError?

StagehandDefaultError is a wrapper Stagehand has used when an underlying operation fails. In issue #676, its full error was Invalid response schema during act or extract. The wrapper alone is not a retry policy. Log the nested message, classify the failed layer, and retry only when that class is transient.

Why does Stagehand say invalid response schema?

The error means structured model output did not match the shape Stagehand expected. Issue #676 reported it as intermittent, so one retry is reasonable. If it repeats, stop and inspect the instruction, model, extraction schema, and captured history. The issue is closed as not planned and provides no maintainer workaround.

Why does my Stagehand agent stop early?

First check whether the agent reached its step budget. Stagehand's current guide says the default maxSteps is 20. Increase it when history shows useful progress, or split a long task into smaller executions and check success after each. A completed promise does not prove the requested browser outcome completed.

How should I handle a Stagehand timeout?

Identify which clock expired. Re-observe after an action-resolution timeout. Retry a fresh session once after waitForFirstTopLevelPage. Increase domSettleTimeout only for DOM stabilization after a page exists. For serverless jobs, also set enough function duration, but do not confuse that outer limit with Stagehand's internal startup timeout.

Should I retry every Stagehand production error?

No. Retry an intermittent model response or fresh browser startup once. Re-observe an unresolved action, and change the plan when an agent exhausts its steps. Never retry missing modules, absent browser helpers, invalid credentials, or other fixed deployment faults per request. The next attempt runs the same broken artifact.

Share this post

The QA column is no longer where the sprint goes to die.

Move one ticket. Watch it come back tested. Then decide.

Keep reading

All posts →