跳到主要内容
知仓学习社ZHICANG

browser-edge-cases

SOP for debugging browser automation failures on complex websites. Use when browser tools fail on specific sites like LinkedIn, Twitter/X, SPAs, or …

联网无严重或高危命中aden-hive/hive

它会碰到什么

扫了多少12 个文本文件,80 KB
它会碰到什么联网
命中总数4 处
命中统计严重 0 · 高 0 · 中 0 · 低 4

这一栏是扫描器报的事实,不是结论。命中多不等于有毒(安全工具、规则库、示例脚本本来就会包含危险写法),命中少也不等于干净。它和你手上的凭据、文件、网络有什么关系,需要你自己看。

技能内容

Browser Tool Edge Cases

Standard Operating Procedure for debugging and fixing browser automation failures on complex websites.

When to Use This Skill

  • browser_interact(action="scroll") succeeds but page doesn't move
  • browser_interact(action="left_click") succeeds but no action triggered
  • browser_interact(action="type") text disappears or doesn't work
  • browser_snapshot hangs or returns stale content
  • browser_navigate loads wrong content

SOP: Debugging Browser Tool Failures

Phase 1: Reproduce & Isolate

1. Create minimal test case demonstrating failure
2. Test against simple site (example.com) to verify tool works
3. Test against problematic site to confirm issue

Quick isolation test:

# 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

Phase 2: Analyze Root Cause

Step 2a: Check console for errors

console = await browser_console(tab_id)
# Look for: CSP violations, React errors, JavaScript exceptions

Step 2b: Inspect DOM structure

html = 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 |

Phase 3: Implement Multi-Layer Fix

Pattern: Always have fallbacks

async 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

# 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)

Phase 4: Verify Fix

1. Run against problematic site → should work
2. Run against simple site → should still work (regression check)
3. Document in registry.md

Pattern Library

P1: Nested Scrollable Containers

Sites: LinkedIn, Twitter/X, any SPA with scrollable feeds

Detection:

// 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.

P2: Element Covered by Overlay

Sites: Modals, tooltips, SPAs with loading overlays

Detection:

const 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.

P3: React Synthetic Events

Sites: React SPAs, modern web apps

Detection: If CDP click doesn't trigger handler but manual click works.

Fix: Use JavaScript click as primary:

element.click();

P4: Huge DOM / Accessibility Tree

Sites: LinkedIn, Facebook, Twitter (feeds with 1000s of nodes)

Detection:

document.querySelectorAll('*').length > 5000

Fix:

  1. Add timeout to snapshot operation
  2. Truncate tree at 2000 nodes
  3. Fall back to DOM-based snapshot if accessibility tree too large

P5: SPA Hydration Delay

Sites: React, Vue, Angular SPAs after navigation

Detection:

// Check if React app has hydrated
document.querySelector('[data-reactroot]') ||
document.querySelector('[data-reactid]')

Fix: Wait for specific selector after navigation:

await 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)

P6: Shadow DOM

Sites: Components using Shadow DOM, Lit elements

Detection:

document.querySelectorAll('*').some(el => el.shadowRoot)

Fix: Pierce shadow root:

function 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;
}

Quick Reference

| 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 |

References

  • [registry.md](registry.md) - Full list of known edge cases
  • [scripts/test_case.py](scripts/test_case.py) - Template for testing new cases
  • [BROWSER_USE_PATTERNS.md](../../tools/BROWSER_USE_PATTERNS.md) - Implementation patterns from browser-use

想直接用这个技能?

本站把开放许可(MIT / Apache 等)的技能按仓库打包整理到网盘,点一下转存到你自己的网盘,不用一个个从 GitHub 拉。许可未声明的技能只给原始仓库链接,不打包。