-
Notifications
You must be signed in to change notification settings - Fork 4.7k
browser problem or model problem
When an agent task fails, one of two very different things broke: the browser side (the page never arrived, or arrived hostile) or the model side (the page was fine and the model mishandled it). The fixes have nothing in common - network and identity on one side, model choice and instructions on the other - so misdiagnosing sends you shopping in the wrong store. People upgrade to a frontier model to fix a blocked page, and swap proxies to fix a model that clicks the wrong button, and both spend money to change nothing.
There is an unusually clean way to split the two, and it costs nothing in
model tokens: replay the failing step on the same engine with no model in the
loop at all. The stealth Firefox that AIHawk drives is on PyPI as a plain
Python library (pip install invisible-playwright, Playwright's API), so a
model-free replay is a few lines in a Python shell. Whatever happens in that
replay happened without a model, so it cannot be a model problem. That is the
instrument this page is built around.
This exact replay was executed against the public scraping sandbox while
writing this page, and it prints Travel | 11 results:
from invisible_playwright import InvisiblePlaywright
with InvisiblePlaywright(seed=7) as browser:
page = browser.new_page()
page.goto("https://books.toscrape.com/", wait_until="domcontentloaded")
page.click('a[href*="category/books/travel"]')
page.locator('h1:text("Travel")').wait_for()
print(page.locator("h1").inner_text(), "|",
page.locator("form strong").first.inner_text(), "results")Each call is the same engine, the same fingerprint work, the same connection
path the agent would use - minus the model. One line of hard-won detail is in
there on purpose: after a click that navigates, wait for something the
DESTINATION shows (wait_for() on its heading) before reading. The first
version of this snippet read the old page's heading and reported the wrong
page confidently - which is exactly the kind of off-by-one a model in the
loop would have papered over.
Swap in your own URL, your own selectors, and the failing step, and you can see verbatim what the browser saw with zero model behavior mixed in.
These appear with any model, and with no model:
-
The page never loads.
page.gototimes out or errors in the model-free replay too. Network, proxy, or the site itself. If a proxy is configured, suspect it first; if this is the first run ever, see the last symptom below. -
A challenge page or block message appears instead of the content.
page.locator("body").inner_text()in the replay shows you verbatim what the site served. If the block is there before any automation logic has acted, no model change can touch it - work through why does my AI agent get blocked?, which separates fingerprint, IP reputation, volume and rhythm. - It works by hand in your normal browser, but not through AIHawk, on the same network. That narrows it to the agent's exit or identity rather than the site being down. The blocked page's checklist is the map.
- The action happened and hit the wrong element. This is its own family and it is mostly neither the browser nor the model: the page moved between the look and the act. Why did the AI agent click the wrong thing has the six causes in the order worth checking, and when the page changes under the AI agent covers the first and commonest of them.
- It worked for many pages, then stopped. Volume or retries, not intelligence. Check the transcript for a retry burst; retry loops and rate limits is that failure's own page.
-
The very first instruction ever answers that the engine is downloading.
Not a failure at all: the browser engine, roughly a quarter of a gigabyte, is
fetched by the server when it starts, and
browser_openreports the progress until it is there.uvx invisible-playwright fetchin a terminal gets it over with where you can watch it.
These appear only with a model in the loop, on pages the model-free replay handles fine:
- The right page, the wrong element. The transcript shows the page loaded and the model clicked or typed somewhere defensible but wrong. Often a field-mapping problem - the forms page covers why look-alike fields invite it.
- Loops. The same action, or the same failing submit, repeated with no change in between. A model that does not register that its last action changed nothing.
- Giving up, or declaring victory early. An answer that does not match what the live pane showed, or a "done" with steps visibly left.
- Misreading the task. It did something coherent, just not what you asked. Usually fixable with a more explicit instruction before it is a reason to change models.
- A run that never converges. The loop has no turn ceiling: it takes turns until the model stops calling tools, so a model going in circles goes in circles until a person ends it. That person is you, watching the live pane. Stop the run when the steps stop making progress, then reissue a narrower instruction rather than the same one. On a genuinely long task a long run is the task's shape; on a short one it is the model wandering, and the transcript tells you which, because wandering repeats itself.
- Unreadable tool arguments. The transcript notes the model's arguments were not valid JSON and it was told to retry. Occasional is tolerable; frequent is a model quality signal in itself.
- Read the failed transcript first. The interface shows each step, what was called, and what came back; on the assistant path the same record is your assistant's own conversation. Most failures are legible there, and the split is often obvious: a block page in a tool result is browser-side, a wrong click on a healthy page is model-side.
-
Replay the failing step with no model. In a Python shell, on the
library:
gotothe same URL, read what came back,clickthe same selector (the executed snippet above is the template). If the failure reproduces, it is browser-side, full stop - no model was present. If your hand-driven steps sail through, the page is drivable and the model is the variable. -
Same task, two models. If step 2 cleared the browser, run the identical
instruction with
--modelset to something stronger, and pass the same--seedboth times so the browser identity is constant and the model is the only thing you moved. One model failing where another succeeds, on the same page and identity, is the clean model-side verdict - and the moment to read which model to use. - Change one thing at a time. Swapping model and proxy together tells you nothing whichever way it goes. This is the same discipline as the blocked checklist, because it is the same trap.
The two sides feed each other. A page that starts refusing mid-task makes a competent model look lost, because every read comes back strange; and a model that reacts to failure by hammering retries turns one soft refusal into a hard block, which then greets the next run too. If a transcript shows both, fix the browser side first: it is upstream, and model behavior on a hostile page is not evidence about the model. Then rerun before judging anything else.
Two instruments help before you get that far. The live view shows the window the run actually had, pointer and tab strip included, which settles several of these in one frame: watching the agent work. And if the split you are chasing is that it works while you watch and fails when you do not, that is its own six-cause list: the task works headed and fails headless.
How do I know if my agent failed because of the site or the model? Replay the failing step on the library with no model in the loop. Reproduces without a model: browser side. Works by hand: model side. That single test settles most cases.
The page shows a challenge or block - which side is that? Browser side, always: it was served before any model decision mattered. Work through the blocked page; changing models changes nothing there.
The agent clicks the wrong thing - which side? Model side, if the transcript shows the page loaded correctly. Try a sharper instruction first, then a stronger model on the same task and seed.
The run keeps going and never finishes - how do I stop it? From the interface: while work is in flight the send button becomes a red stop button, and pressing it cancels the run. One detail worth knowing before you go looking for it: the button is a stop button only while the message box is empty, so if you have typed something the same button queues that message for the next turn instead - clear the box and the stop button is back. The cancel lands at the next tool call, so the step already in flight finishes first. Nothing else ends a run: the loop has no turn ceiling, so on a short task that will not converge, stopping it and narrowing the instruction is the move.
Can I run this diagnosis without spending anything? The replay half, yes - the library needs no model and no key, and the engine download is one-time. The two-model comparison spends normal task tokens on each run.
Is the replay still useful if I already have a key? Precisely because it removes the model: any failure it reproduces is guaranteed browser-side, which is a certainty no model-driven run gives you.
All retrieved 2026-09-03, except the loop itself, re-read 2026-09-08.
-
feder-cr/aihawk_mcp_server, this repository's
source:
src/aihawk/agent.py(the shared loop, which runs until the model stops calling tools and has no turn ceiling, and the invalid-arguments retry),src/aihawk/web.py(the task handle, the stop button and the cancel that lands at the next tool call), and the README (the engine download and prefetch command). - invisible_playwright, the engine as a library; the replay snippet above was executed against books.toscrape.com on 2026-09-03 and printed the line quoted.
See also: why does my AI agent get blocked?, which model to use with AIHawk, and agent retry loops and rate limits.
From the AIHawk wiki. The model-free replay is how the maintainer runs this exact split while debugging; the wrong first version of the snippet above is left described because that is how it went.
- OpenAI Operator alternatives
- Open-source Operator-style agents
- Is OpenAI Operator still available?
- OpenAI Operator vs Claude computer use
- browser-use alternatives
- Choosing an AI browser agent
- Open-source AI browser agents
- Open-source computer-use agents
- What is an AI web agent?
- AI browser agents vs traditional scraping
- Cloud browser infrastructure for AI agents, explained
- Browserbase alternatives
- Firecrawl vs an AI browser agent
- Skyvern alternatives
- Stagehand vs browser-use
- Project Mariner is gone: what replaced it
- Manus alternatives
- Gemini computer use vs Claude computer use
- AIHawk, reviewed honestly by its own wiki
- AI browser vs AI browser agent: which one do you want?
- AI browser agent vs RPA: which one fits the job
- AI browser agent vs n8n, Zapier and Make
- Vercel agent-browser alternatives, compared honestly
- What is an agentic browser? Definition and the two kinds
- Open-source agentic browsers: the three layers, compared
- Choosing an MCP server for browser automation: four axes
- Stealth MCP servers compared: Camoufox, nodriver, Patchright
- Playwright MCP alternatives, and the three you don't need
- Autonomous browser agents: the four rungs of autonomy
- What is actually free in the AI browser agent stack
- browser-use on GitHub: what the repo actually gives you
- Playwright MCP vs Chrome DevTools MCP: different jobs
- How to choose among MCP servers: a map by category
- Which MCP servers are worth adding to Claude Code
- MCP on GitHub: finding servers and judging them fast
- MCP vs an API: the decision, and what the wrapper costs
- MCP alternatives: when the protocol is the wrong shape
- Why does my AI agent get blocked?
- The timing signal AI agents give off
- Agent retry loops trip rate limits, not fingerprints
- Claude computer use detected as a bot
- browser-use getting blocked: what you can and cannot change
- Playwright MCP session blocked: four causes, four fixes
- Playwright MCP and captchas: what actually gets you past
- Cloudflare and a browser MCP server: what is being read
- Can an AI agent solve a captcha? The honest answer
- Getting an AI agent to fill out forms
- Which model to use with AIHawk
- Browser problem or model problem?
- Running AIHawk's browser from Claude Code
- Extracting data to a CSV with an AI agent
- Monitoring a page for changes with an AI agent
- Running AIHawk's browser from Claude Desktop
- Running AIHawk's browser from Cursor
- Using an AI agent to hunt for apartments
- Getting website data into Google Sheets with an AI agent
- Using an AI agent to download invoices from portals
- AI agents for web research
- Using an AI agent to test your own website
- Running AIHawk's browser from Cline
- Posting to social media with an AI agent
- Posting to Facebook with an AI agent
- Posting to Instagram with an AI agent
- Posting to X with an AI agent
- Automating LinkedIn posts: read this first
- Appointment bots: what they are and what an agent can legitimately do
- Track prices across sites with an AI agent
- Build a lead list with an AI browser agent
- Run an AI browser agent on a schedule
- AI browser agent with a local LLM: what changes
- Should you log your AI agent into your accounts?
- How to write a task an AI browser agent can follow
- Move data between two web apps with an AI agent
- The MCP server
- How the tools are shaped, and why
- Playwright MCP vs the Playwright CLI: which fits when
- Playwright MCP: browser is already in use, and the fix
- Playwright MCP best practices: four decisions that matter
- Playwright MCP with a proxy, and the three leaks it leaves
- A browser MCP server in GitHub Copilot: setup and limits
- Using a browser MCP server for web scraping: the pattern
- Which LLM for browser automation: the four properties
- How to build a browser agent, and what to take instead
- Getting an AI agent to log into a website: three routes
- MCP tools, resources and prompts: who controls each
- How many MCP tools is too many? The context arithmetic
- How to build an MCP server: the decisions, not the scaffold
- Local or remote MCP server: what changes, and what does not
- Writing an MCP client in Python: the thirty-line version
- Self-hosted AI agent: what one actually costs to run
- How long an AI browser agent takes per step, measured
- Text, HTML, snapshot or screenshot: what the agent should read
- Giving an AI browser agent a stopping condition
- Keeping an AI browser agent out of destructive actions
- Why did the AI agent click the wrong thing
- When the page changes under the AI agent
- Running one AI agent task across a list of sites
- Seeing a page as it appears in another country
- Getting data out of a dashboard with no export button
- Two browsers in one session: main and support
- Finding the dead links on a site with an AI agent
- Filling a CRM record from a company's website
- One form submission per spreadsheet row, with an AI agent
- Dated screenshots of a page as evidence
- Checking order and delivery status with an AI agent
- Reading a PDF that opens inside the browser
- Summarising a long page or thread with an AI agent
- Collecting every image on a page with its caption
- Collecting event and course listings with an AI agent
- Cancelling a subscription with an AI agent
- What an AI agent can and cannot do inside an iframe
- Shadow DOM and an AI agent: you can click it, you cannot read it
- Running AIHawk's browser from Codex
- What a page snapshot costs, per control
- Native selects and the ones that only look like selects
- Running AIHawk's browser from Gemini CLI
- Clicking by selector or by coordinates
- How long the agent waits before it gives up
- What a second browser costs
- Uploading a file with an AI agent, and why this one cannot
- Watching the agent work, and when it is worth it
- When not to use an AI browser agent
- Agent or script: deciding once instead of every time
- Using the keyboard instead of the mouse
- Secrets in an agent task: where they end up
- What an agent run should log
- Deduplicating what an AI agent collects
- Normalising values across sites
- Validating an AI agent's output
- Reading a table with an AI agent
- Driving a site's own search and filters
- The task works headed and fails headless