From d6f3c2d48b3f6846743907c03b89305d09b3689c Mon Sep 17 00:00:00 2001 From: Philippe Date: Fri, 4 Sep 2026 17:04:36 +0200 Subject: [PATCH] fix(browser): let quit clean up a browser whose contexts were never built When start raises after Client.new but before Contexts.new returns, the browser keeps a non-nil @client and a nil @contexts. quit passes its @client guard, then calls contexts.close_connections and dies with NoMethodError before resetting any state, so restart can never recover. Guarding with safe navigation lets quit close the client, stop the process and clear the ivars, which is exactly what restart needs. --- lib/ferrum/browser.rb | 2 +- spec/browser_spec.rb | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/ferrum/browser.rb b/lib/ferrum/browser.rb index 4cea1153..1c265121 100644 --- a/lib/ferrum/browser.rb +++ b/lib/ferrum/browser.rb @@ -249,7 +249,7 @@ def restart def quit(wait: true) return unless @client - contexts.close_connections + @contexts&.close_connections @client.close thread = @process.stop(wait: wait) diff --git a/spec/browser_spec.rb b/spec/browser_spec.rb index dfea13fa..14a212e3 100644 --- a/spec/browser_spec.rb +++ b/spec/browser_spec.rb @@ -495,6 +495,20 @@ def proxy_tunnel(socket, request, browser_url) ensure browser&.quit end + + it "recovers when a previous start died after the client was built" do + browser = Ferrum::Browser.new + + allow(Ferrum::Contexts).to receive(:new).and_raise(Ferrum::DeadBrowserError) + expect { browser.restart }.to raise_error(Ferrum::DeadBrowserError) + + allow(Ferrum::Contexts).to receive(:new).and_call_original + + expect { browser.restart }.not_to raise_error + expect(browser.contexts).not_to be_nil + ensure + browser&.quit + end end describe "#resize" do