From 8d28f23efbd653edd0b9a7615d6d6f090390df9d Mon Sep 17 00:00:00 2001 From: Buns0705 <111303583+Buns0705@users.noreply.github.com> Date: Sat, 4 Jul 2026 04:53:46 +0700 Subject: [PATCH] fix(browser): keep shared browser handle on a long-lived context (#1340) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- pkg/browser/browser.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pkg/browser/browser.go b/pkg/browser/browser.go index 8db1726f..1dff9616 100644 --- a/pkg/browser/browser.go +++ b/pkg/browser/browser.go @@ -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 {