Context
Follow-up from #337, which added close_tab(target=None) to src/browser_harness/helpers.py. Merged with this known sharp edge.
Problem
switch_tab() registers the active tab/session in the daemon via:
_send({"meta": "set_session", "session_id": sid, "target_id": target_id})
close_tab() calls Target.closeTarget directly and does not clear or update that state. So when you call close_tab() with no args (closing the currently-attached tab), the daemon still believes that now-dead targetId / sessionId is active.
Subsequent calls that depend on the attached session — js(), page_info(), current_tab(), _mark_tab(), etc. — will hit a dead session and fail or misbehave until the caller manually switch_tab()s to another tab.
The bulk-cleanup example in the PR description sidesteps this by only closing non-current tabs, but the no-arg close_tab() shape is the obvious footgun.
Repro
new_tab("https://example.com")
close_tab() # closes the attached tab
page_info() # boom — session points at a closed target
Suggested fix
When close_tab() is closing the currently-attached tab, fall back to another real tab before issuing Target.closeTarget:
- If
list_tabs(include_chrome=False) has another tab, switch_tab() to it first, then close the old targetId.
- If no other real tab exists, either (a) open a fresh
about:blank and switch to it, or (b) clear the daemon session and document that the caller must switch_tab() / new_tab() before further calls.
Option (a) matches the spirit of ensure_real_tab() and keeps the harness in a usable state. Option (b) is simpler but pushes the burden onto callers.
Either way, add a short note to the docstring.
Scope
src/browser_harness/helpers.py — update close_tab().
- Worth a small test: open two tabs, close current, assert that subsequent
current_tab() / js() works.
Context
Follow-up from #337, which added
close_tab(target=None)tosrc/browser_harness/helpers.py. Merged with this known sharp edge.Problem
switch_tab()registers the active tab/session in the daemon via:close_tab()callsTarget.closeTargetdirectly and does not clear or update that state. So when you callclose_tab()with no args (closing the currently-attached tab), the daemon still believes that now-deadtargetId/sessionIdis active.Subsequent calls that depend on the attached session —
js(),page_info(),current_tab(),_mark_tab(), etc. — will hit a dead session and fail or misbehave until the caller manuallyswitch_tab()s to another tab.The bulk-cleanup example in the PR description sidesteps this by only closing non-current tabs, but the no-arg
close_tab()shape is the obvious footgun.Repro
Suggested fix
When
close_tab()is closing the currently-attached tab, fall back to another real tab before issuingTarget.closeTarget:list_tabs(include_chrome=False)has another tab,switch_tab()to it first, then close the oldtargetId.about:blankand switch to it, or (b) clear the daemon session and document that the caller mustswitch_tab()/new_tab()before further calls.Option (a) matches the spirit of
ensure_real_tab()and keeps the harness in a usable state. Option (b) is simpler but pushes the burden onto callers.Either way, add a short note to the docstring.
Scope
src/browser_harness/helpers.py— updateclose_tab().current_tab()/js()works.