vercel-labs/agent-browser · error

Screenshot did not return a file path.

Error message

Screenshot did not return a file path.

What it means

This throw lives in the example code of the vercel-sandbox skill (skill-data/vercel-sandbox/SKILL.md:53), not in library code. After running `agent-browser screenshot` through runAgentBrowserCommand, the snippet reads ssResult.json?.data?.path and throws when no path string came back. A missing path means the command's stdout was not the expected JSON payload (parseJson in shared.ts returns null on bad JSON) or the JSON lacked data.path.

Source

Thrown at skill-data/vercel-sandbox/SKILL.md:53

## Screenshot

The `screenshot --json` command saves to a file and returns the path. Read the file back as base64:

```ts
export async function screenshotUrl(url: string) {
  return withBrowser(async (sandbox) => {
    await runAgentBrowserCommand(sandbox, ["open", url]);

    const titleResult = await runAgentBrowserCommand<{ data?: { title?: string } }>(sandbox, [
      "get", "title",
    ]);
    const title = titleResult.json?.data?.title || url;

    const ssResult = await runAgentBrowserCommand<{ data?: { path?: string } }>(sandbox, [
      "screenshot",
    ]);
    const ssPath = ssResult.json?.data?.path;
    if (!ssPath) throw new Error("Screenshot did not return a file path.");
    const b64Result = await sandbox.runCommand("base64", ["-w", "0", ssPath]);
    const screenshot = (await b64Result.stdout()).trim();

    await runAgentBrowserCommand(sandbox, ["close"], { json: false });

    return { title, screenshot };
  });
}
```

## Accessibility Snapshot

```ts
export async function snapshotUrl(url: string) {
  return withBrowser(async (sandbox) => {
    await runAgentBrowserCommand(sandbox, ["open", url]);

    const titleResult = await runAgentBrowserCommand<{ data?: { title?: string } }>(sandbox, [

View on GitHub (pinned to 548b159b30)

Solutions

  1. Log ssResult.stdout, ssResult.stderr, and ssResult.exitCode right before the throw to see what the CLI actually returned; that distinguishes bad JSON from a missing field.
  2. Remove any custom installSpec pin so the sandbox installs agent-browser at the version matching @agent-browser/sandbox (DEFAULT_AGENT_BROWSER_INSTALL_SPEC in shared.ts:38), or bump the pinned spec to a version that returns { data: { path } } from screenshot --json.
  3. Ensure the screenshot call keeps JSON mode on: call runAgentBrowserCommand(sandbox, ["screenshot"]) without { json: false }, which would make stdout non-JSON.
  4. Check that a page is actually open in the session (a prior `open` succeeded and throwIfCommandFailed did not fire) before taking the screenshot.

Example fix

// before
const ssResult = await runAgentBrowserCommand<{ data?: { path?: string } }>(sandbox, [
  "screenshot",
]);
const ssPath = ssResult.json?.data?.path;
if (!ssPath) throw new Error("Screenshot did not return a file path.");

// after
const ssResult = await runAgentBrowserCommand<{ data?: { path?: string } }>(sandbox, [
  "screenshot",
]);
const ssPath = ssResult.json?.data?.path;
if (!ssPath) {
  throw new Error(
    `Screenshot did not return a file path (exit=${ssResult.exitCode}): ${ssResult.stderr || ssResult.stdout}`,
  );
}
Defensive patterns

Strategy: type-guard

Validate before calling

const ssResult = await runAgentBrowserCommand<{ data?: { path?: string } }>(sandbox, [
  "screenshot",
]);

if (ssResult.exitCode !== 0) {
  throw new Error(`screenshot failed (exit ${ssResult.exitCode}): ${ssResult.stderr}`);
}
if (ssResult.json === null) {
  throw new Error(`screenshot stdout was not JSON: ${ssResult.stdout.slice(0, 200)}`);
}

Type guard

interface ScreenshotJson {
  readonly data?: { readonly path?: string };
}

function hasScreenshotPath(
  result: AgentBrowserCommandResult<ScreenshotJson>,
): result is AgentBrowserCommandResult<ScreenshotJson> & {
  readonly json: ScreenshotJson & { readonly data: { readonly path: string } };
} {
  return typeof result.json?.data?.path === "string" && result.json.data.path.length > 0;
}

const ssResult = await runAgentBrowserCommand<ScreenshotJson>(sandbox, ["screenshot"]);
if (!hasScreenshotPath(ssResult)) {
  throw new Error(
    `Screenshot did not return a file path (exit=${ssResult.exitCode}): ${ssResult.stderr || ssResult.stdout}`,
  );
}
const ssPath = ssResult.json.data.path; // narrowed to string

Try / catch

try {
  const b64 = await sandbox.runCommand("base64", ["-w", "0", ssPath]);
} catch (error) {
  if (error instanceof Error && error.message.includes("Screenshot did not return a file path")) {
    // inspect ssResult.stdout/stderr: usually version drift between helper and CLI
    throw new Error(
      `agent-browser CLI returned unexpected screenshot output. Upgrade the CLI install spec. Raw: ${ssResult.stdout.slice(0, 200)}`,
    );
  }
  throw error;
}

Prevention

When it happens

Trigger: The screenshot command runs with --json appended automatically (buildAgentBrowserArgv in shared.ts:82-84), so this triggers when the agent-browser CLI inside the sandbox prints non-JSON output (older CLI versions, warnings mixed into stdout), returns a JSON shape without data.path, or when json parsing yields null because stdout is empty (e.g. the command failed in a way that bypassed throwIfCommandFailed, or the CLI version installed via a custom installSpec predates the path-in-JSON behavior).

Common situations: Version drift: @agent-browser/sandbox helpers expect the JSON contract of the matching agent-browser CLI, but installSpec pins an older CLI; stdout polluted by shell warnings from the sandbox VM; a CLI that prints the raw path as plain text instead of JSON; invoking the snippet against a session where the screenshot silently failed.

Related errors


AI-assisted analysis of vercel-labs/agent-browser@548b159b30 (2026-08-16). Data as JSON: /api/errors/efac5247b248ce6b. Report an issue: GitHub.