fix(browser): keep shared browser handle on a long-lived context (#1340)

Start() bound the shared *rod.Browser to a context built with
context.WithTimeout(ctx, 15s) and a deferred cancel, so the browser's
context was canceled the moment Start() returned. Any later reuse of the
shared handle then failed immediately with "context canceled" — most
visibly m.browser.Incognito() in tenantBrowserLocked, so a successful
"start" action was followed by a failing tab creation for scoped
(tenant|user|agent) agent sessions. The getPage path masked this via its
auto-reconnect (reconnectLocked uses a background context), but the
incognito path has no such recovery.

Connect with the default background context, the same way reconnectLocked
already does. The local launcher path stays bounded by launchCtx and
remote reachability by resolveRemoteCDP, so no meaningful connect-time
guard is lost.
This commit is contained in:
Buns0705 authored and GitHub committed 2026-07-04 04:53:46 +07:00
1 parent d913eb2149
commit 8d28f23efb
1 file changed
+9 -4
+9 -4
View File
@@ -163,10 +163,15 @@ func (m *Manager) Start(ctx context.Context) error {
m.logger.Info("Chrome launched", "cdp", controlURL, "headless", m.headless, "pid", l.PID())
}
connectCtx, connectCancel := context.WithTimeout(ctx, 15*time.Second)
defer connectCancel()
b := rod.New().Context(connectCtx).ControlURL(controlURL)
// Connect using a long-lived background context, the same way reconnectLocked
// does. Binding m.browser to a timeout/request context here would cancel it
// the moment Start returns (via defer), leaving the shared browser handle with
// a dead context. Later operations that reuse it — notably m.browser.Incognito()
// in tenantBrowserLocked — then fail immediately with "context canceled", which
// is why a successful "start" is followed by a failing "open"/tab creation.
// resolveRemoteCDP already bounds remote reachability, and the local launcher
// path is bounded by launchCtx above.
b := rod.New().ControlURL(controlURL)
if err := b.Connect(); err != nil {
// If local launch succeeded but connect failed, kill the orphan process
if m.launcher != nil {