Gstack Error Philosophy for AI Agents: Treating Failures as Machine-Readable Instructions

Gstack treats runtime errors as machine-readable guidance for AI agents, rewriting every Playwright failure into an actionable hint that tells the agent exactly what to try next without exposing internal stack traces.

The garrytan/gstack repository implements a deterministic approach to error handling designed specifically for autonomous AI workflows. Unlike traditional debugging logs meant for human developers, the gstack error philosophy for AI agents transforms low-level browser failures into programmatic instructions. This enables AI agents to recover from errors independently, making automated browsing robust and reproducible without human intervention.

Core Principles of Agent-Centric Error Handling

Agent-First Message Design

Every error message emitted by the browse server is phrased as a directive an agent can act upon immediately. According to ARCHITECTURE.md, specific failure modes map to concrete recovery steps:

  • Element not found → "Element not found or not interactable. Run snapshot -i to see available elements."
  • Multiple matches → "Selector matched multiple elements. Use @refs from snapshot instead."
  • Timeout → "Navigation timed out after 30 s. The page may be slow or the URL may be wrong."

Uniform Error Rewriting

Playwright's native errors are intercepted and categorized before reaching the agent. The wrapError() function in browse/src/server.ts strips internal stack traces, classifies the failure type, and appends a concrete hint the agent can execute.

Deterministic JSON Error Payloads

When operations fail, the server returns a structured JSON payload containing both error and hint fields. This eliminates ambiguity and allows the agent to branch its logic programmatically, either retrying with a corrected selector, capturing a snapshot for debugging, or aborting the workflow.

Implementation in browse/src/server.ts

The error transformation logic lives in browse/src/server.ts. The wrapError() function inspects raw Playwright exceptions and returns agent-friendly strings:

// browse/src/server.ts – wrapError()
function wrapError(err: any): string {
  const msg = err.message || String(err);
  // Timeout handling
  if (err.name === 'TimeoutError' || msg.includes('Timeout')) {
    if (msg.includes('locator.click')) {
      return `Element not found or not interactable within timeout. ` +
             `Check your selector or run 'snapshot' for fresh refs.`;
    }
    if (msg.includes('page.goto')) {
      return `Page navigation timed out. The URL may be unreachable or the page may be loading slowly.`;
    }
    return `Operation timed out: ${msg.split('\n')[0]}`;
  }
  // Multiple matches
  if (msg.includes('resolved to') && msg.includes('elements')) {
    return `Selector matched multiple elements. Be more specific or use @refs from 'snapshot'.`;
  }
  // Fallback – pass through other errors
  return msg;
}

How Agents Consume Error Hints

Agents check the hint field to determine recovery strategies without parsing raw logs. The following pseudo-code demonstrates how a skill might react to an error payload:

// Pseudo-code used by a skill
const result = await invokeBrowse('click', ['#submit']);
if (result.error) {
  console.log('Agent hint:', result.hint);
  // Example: if hint mentions `snapshot`, run it and retry
  if (result.hint.includes('snapshot')) {
    await invokeBrowse('snapshot', ['-i']);
    // Re-try with a new selector derived from the snapshot
  }
}

Crash Handling Philosophy

Fatal browser crashes—such as Chromium disconnects—trigger immediate server exit rather than complex self-healing logic. The CLI detects the dead server on the next command and restarts it. This design avoids indeterminate state inside the browse server and keeps the error model predictable for agents.

Summary

  • Machine-readable guidance: All errors are rewritten as actionable instructions rather than human-focused diagnostics.
  • Uniform rewriting: The wrapError() function in browse/src/server.ts intercepts Playwright errors to strip stack traces and add hints.
  • Structured payloads: Errors return JSON with error and hint fields enabling programmatic recovery workflows.
  • Deterministic crashes: Fatal errors cause immediate exit, allowing external orchestration to restart the service cleanly.

Frequently Asked Questions

How does gstack handle Playwright timeout errors?

Gstack intercepts TimeoutError exceptions in the wrapError() function and categorizes them based on the operation type. For locator.click timeouts, it suggests checking selectors or running snapshot; for page.goto timeouts, it indicates potential URL or network issues.

What fields are included in a gstack error response?

Every error response includes an error field describing the failure and a hint field containing a specific directive the AI agent can execute, such as running snapshot -i to list available elements.

Why does gstack exit immediately on browser crashes instead of attempting recovery?

Immediate exit on fatal crashes—such as Chromium disconnects—avoids complex self-healing logic inside the server. The CLI detects the dead process on the next command and handles restart, maintaining a simple, deterministic error model that agents can rely upon.

Where is the error philosophy documented across the gstack repository?

The error philosophy is formally defined in ARCHITECTURE.md under the "Error philosophy" section, implemented in browse/src/server.ts within the wrapError() function, and referenced by skill workflows in files such as review/SKILL.md and guard/SKILL.md.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →