Install any skill in seconds. Free to start, no credit card required.
Get Started Free →SOP for debugging browser automation failures on complex websites. Use when browser tools fail on specific sites like LinkedIn, Twitter/X, SPAs, or sites with Shadow DOM.
.claude/skills/aden-hive-browser-edge-cases/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 26% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 31% | 0% |
| case-09 | ✗→✓ | ▲ Improved | -2% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 2% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 66% | 0% |
Standard Operating Procedure for debugging and fixing browser automation failures on complex websites.
browser_interact(action="scroll") succeeds but page doesn't movebrowser_interact(action="left_click") succeeds but no action triggeredbrowser_interact(action="type") text disappears or doesn't workbrowser_snapshot hangs or returns stale contentbrowser_navigate loads wrong content1. Create minimal test case demonstrating failure
2. Test against simple site (example.com) to verify tool works
3. Test against problematic site to confirm issueQuick isolation test:
python# Test 1: Does the tool work at all? await browser_navigate(tab_id, "https://example.com") result = await browser_interact(action="scroll", tab_id=tab_id, scroll_direction="down", scroll_amount=100) # Should work on simple sites # Test 2: Does it fail on the problematic site? await browser_navigate(tab_id, "https://linkedin.com/feed") result = await browser_interact(action="scroll", tab_id=tab_id, scroll_direction="down", scroll_amount=100) # If this fails but example.com works → site-specific edge case
Step 2a: Check console for errors
pythonconsole = await browser_console(tab_id) # Look for: CSP violations, React errors, JavaScript exceptions
Step 2b: Inspect DOM structure
pythonhtml = await browser_html(tab_id) snapshot = await browser_snapshot(tab_id) # Look for: # - Nested scrollable divs (overflow: scroll/auto) # - Shadow DOM roots # - iframes # - Custom widgets
Step 2c: Identify the pattern
| Symptom | Likely Cause | Check | |---------|--------------|-------| | Scroll doesn't move | Nested scroll container | Look for overflow: scroll divs | | Click no effect | Element covered | Check getBoundingClientRect vs viewport | | Type clears | Autocomplete/React | Check for event listeners on input; try a type action with no selector | | Snapshot hangs | Huge DOM | Check node count in snapshot | | Snapshot stale | SPA hydration | Wait after navigation |
Pattern: Always have fallbacks
pythonasync def robust_operation(tab_id): # Method 1: Primary approach try: result = await primary_method(tab_id) if verify_success(result): return result except Exception: pass # Method 2: CDP fallback try: result = await cdp_fallback(tab_id) if verify_success(result): return result except Exception: pass # Method 3: JavaScript fallback return await javascript_fallback(tab_id)
Pattern: Always add timeouts
python# Bad - can hang forever result = await browser_snapshot(tab_id) # Good - fails fast with useful error try: result = await browser_snapshot(tab_id, timeout_s=10.0) except asyncio.TimeoutError: # Handle timeout gracefully result = await fallback_snapshot(tab_id)
1. Run against problematic site → should work
2. Run against simple site → should still work (regression check)
3. Document in registry.mdSites: LinkedIn, Twitter/X, any SPA with scrollable feeds
Detection:
javascript// Find largest scrollable container const candidates = []; document.querySelectorAll('*').forEach(el => { const style = getComputedStyle(el); if (style.overflow.includes('scroll') || style.overflow.includes('auto')) { const rect = el.getBoundingClientRect(); if (rect.width > 100 && rect.height > 100) { candidates.push({el, area: rect.width * rect.height}); } } }); candidates.sort((a, b) => b.area - a.area); return candidates[0]?.el;
Fix: Dispatch scroll events at container's center, not viewport center.
Sites: Modals, tooltips, SPAs with loading overlays
Detection:
javascriptconst rect = element.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; const topElement = document.elementFromPoint(centerX, centerY); return topElement === element || element.contains(topElement);
Fix: Wait for overlay to disappear, or use JavaScript click.
Sites: React SPAs, modern web apps
Detection: If CDP click doesn't trigger handler but manual click works.
Fix: Use JavaScript click as primary:
javascriptelement.click();
Sites: LinkedIn, Facebook, Twitter (feeds with 1000s of nodes)
Detection:
javascriptdocument.querySelectorAll('*').length > 5000
Fix:
Sites: React, Vue, Angular SPAs after navigation
Detection:
javascript// Check if React app has hydrated document.querySelector('[data-reactroot]') || document.querySelector('[data-reactid]')
Fix: Wait for specific selector after navigation:
pythonawait browser_navigate(tab_id, url, wait_until="load") await browser_interact(action="wait", tab_id=tab_id, wait_for_selector='[data-testid="content"]', timeout_ms=5000)
Sites: Components using Shadow DOM, Lit elements
Detection:
javascriptdocument.querySelectorAll('*').some(el => el.shadowRoot)
Fix: Pierce shadow root:
javascriptfunction queryShadow(selector) { const parts = selector.split('>>>'); let node = document; for (const part of parts) { if (node.shadowRoot) { node = node.shadowRoot.querySelector(part.trim()); } else { node = node.querySelector(part.trim()); } } return node; }
| Issue | Primary Fix | Fallback | |-------|-------------|----------| | Scroll not working | Find scrollable container | Mouse wheel at container center | | Click no effect | JavaScript click() | CDP mouse events | | Type clears | use_insert_text=False (per-keystroke) | Use a type action (Input.insertText) | | Snapshot hangs | Add timeout_s | DOM snapshot fallback | | Stale content | Wait for selector | Increase wait_until timeout | | Shadow DOM | Pierce selector | JavaScript traversal |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 20,497 | 13,328 | -35% | 1 | 1 | 0% | 3,535 | 4,446 | +26% | 0 | 0 | — |
case-07 | pass→pass | 15,477 | 6,415 | -59% | 1 | 1 | 0% | 3,172 | 3,089 | -3% | 0 | 0 | — |
case-02 | fail→pass | 21,591 | 16,963 | -21% | 1 | 1 | 0% | 3,903 | 5,127 | +31% | 0 | 0 | — |
case-03 | pass→pass | 23,159 | 14,467 | -38% | 1 | 1 | 0% | 4,440 | 4,510 | +2% | 0 | 0 | — |
case-04 | pass→pass | 13,019 | 10,561 | -19% | 1 | 1 | 0% | 2,602 | 4,111 | +58% | 0 | 0 | — |
case-05 | pass→pass | 7,594 | 6,344 | -16% | 1 | 1 | 0% | 1,503 | 2,929 | +95% | 0 | 0 | — |
case-06 | pass→pass | 13,169 | 11,514 | -13% | 1 | 1 | 0% | 2,714 | 3,609 | +33% | 0 | 0 | — |
case-08 | pass→pass | 13,410 | 7,316 | -45% | 1 | 1 | 0% | 2,746 | 3,290 | +20% | 0 | 0 | — |
case-09 | fail→pass | 14,966 | 3,701 | -75% | 1 | 1 | 0% | 2,525 | 2,465 | -2% | 0 | 0 | — |
case-10 | fail→pass | 16,656 | 6,485 | -61% | 1 | 1 | 0% | 3,001 | 3,051 | +2% | 0 | 0 | — |
case-11 | fail→pass | 13,363 | 11,596 | -13% | 1 | 1 | 0% | 2,355 | 3,917 | +66% | 0 | 0 | — |
case-12 | fail→pass | 13,023 | 7,689 | -41% | 1 | 1 | 0% | 2,410 | 3,251 | +35% | 0 | 0 | — |
case-13 | fail→pass | 8,945 | 2,939 | -67% | 1 | 1 | 0% | 1,498 | 2,341 | +56% | 0 | 0 | — |
case-14 | pass→pass | 13,087 | 3,614 | -72% | 1 | 1 | 0% | 2,128 | 2,228 | +5% | 0 | 0 | — |
case-15 | fail→pass | 17,492 | 14,385 | -18% | 1 | 1 | 0% | 3,110 | 4,438 | +43% | 0 | 0 | — |
case-16 | fail→pass | 13,408 | 5,370 | -60% | 1 | 1 | 0% | 2,272 | 2,755 | +21% | 0 | 0 | — |
case-17 | fail→pass | 11,951 | 8,133 | -32% | 1 | 1 | 0% | 1,913 | 3,281 | +72% | 0 | 0 | — |
case-18 | pass→pass | 12,280 | 3,603 | -71% | 1 | 1 | 0% | 2,047 | 2,518 | +23% | 0 | 0 | — |
case-19 | pass→pass | 11,263 | 4,804 | -57% | 1 | 1 | 0% | 1,944 | 2,622 | +35% | 0 | 0 | — |
case-20 | fail→pass | 16,394 | 8,317 | -49% | 1 | 1 | 0% | 2,662 | 3,374 | +27% | 0 | 0 | — |
case-21 | pass→pass | 11,576 | 9,130 | -21% | 1 | 1 | 0% | 2,423 | 3,498 | +44% | 0 | 0 | — |
case-22 | pass→pass | 10,329 | 2,789 | -73% | 1 | 1 | 0% | 2,068 | 2,366 | +14% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +50 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.