Common Errors in Playwright & Puppeteer Script Generator Output
The most frequent failure in browser automation is the timeout on element lookup. Playwright and Puppeteer both wait a bounded period for a selector to become actionable, and when the element never appears the run aborts. The usual culprits are a selector that no longer matches after a redesign, a lazy-loaded component that only renders after scrolling, or an action that happens before the single-page app finishes its async fetch. The fix is almost always a better wait: target a stable element that appears only after loading completes, rather than guessing a fixed sleep.
Strict-mode violations are a signature Playwright error. When
strict: true is set (the
default in test runner mode), a locator that matches more than
one element refuses to act, forcing you to disambiguate. The
message "resolved to 4 elements" means your selector is too
broad — a bare button may
match every button on the page. Narrow it with role, name, or
test-id, or use
.first() deliberately.
This is a feature masquerading as an error: it catches ambiguous
selectors before they cause subtle wrong-element bugs.
Selectors that work in the console but fail in the script
usually point to iframes. Playwright requires explicit frame
stepping:
page.frameLocator('#embed').getByText('...'), while Puppeteer uses
page.frames().find(...)
followed by queries within that frame. An assert that reads
page.textContent('body')
also misses everything inside an iframe, because the top-level
body does not contain the frame's document. If a generated
assertion fails only on pages with embeds, check whether the
expected text lives in a frame.
The networkidle hang is a
modern classic. Waiting for the network to be idle before
interacting sounds safe, but analytics beacons, heartbeats, and
infinite polling feeds can keep the page "busy" almost forever,
turning a working script into a 30-second stall or a timeout.
Playwright teams have largely moved to
'domcontentloaded' or a
wait on a key element instead. If your generated
goto with
networkidle hangs on a
chatty page, switch the wait condition to load or drop it
entirely.
Filling fields that "aren't visible" is another common trap.
Playwright's
fill requires the element
to be visible and enabled; hidden inputs, elements scrolled out
of view, or disabled buttons reject the action. Puppeteer's
type sends keystrokes
even to elements that are merely present, which can produce
silent no-ops. Scroll the element into view (locator.scrollIntoViewIfNeeded()) or interact after a visibility wait so the action lands where
you expect.
Race conditions between navigation and action are a subtle but persistent source of flakiness. Clicking a link and immediately querying the next page can read the old DOM, because navigation is asynchronous. The reliable pattern is to act, then wait for something that can only exist on the new page — the URL, a heading, or a specific element — before asserting. Scripts generated from atomic actions avoid this naturally when each assert step waits on content that follows the previous action.
Text escaping breaks generated code in sneaky ways. A string
containing a single quote inside a single-quoted JavaScript
string, or a double quote inside Python, ends the string early
and produces a syntax error. The TopWebTool generator escapes
quotes and backslashes in every emitted string precisely so
pasted text cannot corrupt the output file — but if you
hand-edit a generated script, keep the same escaping discipline.
A quick
node -c or
python -m py_compile
catches these before execution.
Finally, context mistakes break whole suites: running a headed script on a headless-only CI image, forgetting to install browser binaries in a fresh container, or launching Chromium without system dependencies. These manifest as "Target page, context or browser has been closed" or immediate launch failures. Install browsers once per image, pin versions, and run a single smoke script after image build so environment problems surface at image time, not during a critical run.
Most browser-automation failures reduce to four categories: timing, selectors, frames, and environment. Approach each error as a signal — a timeout says "wait differently," a strict-mode error says "be more specific," a frame miss says "step into the frame," and a launch failure says "fix the runtime." With those reflexes, generated scripts go from flaky experiments to dependable production tools.
Strict-mode violations are the error that surfaces after a refactor. Playwright's default assumption is that a locator should match exactly one element, so when a page suddenly contains two elements that both match — a hidden helper button and the visible one, an icon duplicated in a menu — the run fails with a strict-mode error instead of silently clicking the wrong thing. The remedy is better locators, not looser ones: scope the query, use the visible element, or add a filter for text or state. The analogous class of bugs comes from timeouts: an element that never appears because the page failed, the selector is wrong, or the network stalled. The error message tells you which condition was unmet, and the fix is to make the wait conditional on the true precondition of the next step rather than adding blanket timeouts. Reading these two errors correctly — strict-mode means ambiguity, timeout means the precondition failed — turns most automation debugging into a two-minute investigation instead of an afternoon.