SeleniumHQ/selenium · error · TimeoutError

-> #{@pid} still alive after #{timeout} seconds

Error message

  ->  #{@pid} still alive after #{timeout} seconds

What it means

ChildProcess#poll_for_exit waits for a spawned child (e.g. driver/server process) to exit, polling at POLL_INTERVAL up to the timeout. If the process is still alive past end_time, it raises TimeoutError. This is the process-level companion to socket timeouts.

Source

Thrown at rb/lib/selenium/webdriver/common/child_process.rb:100

        _, @status = waitpid2(@pid, Process::WNOHANG | Process::WUNTRACED) if @status.nil?
        return false if @status.nil?

        exit_code = @status.exitstatus || @status.termsig
        WebDriver.logger.debug("  -> exit code is #{exit_code.inspect}", id: :process)

        !!exit_code
      rescue Errno::ECHILD, Errno::ESRCH
        WebDriver.logger.debug("  -> process: #{@pid} already finished", id: :process)
        true
      end

      def poll_for_exit(timeout)
        WebDriver.logger.debug("Polling #{timeout} seconds for exit of #{@pid}", id: :process)

        end_time = Time.now + timeout
        sleep POLL_INTERVAL until exited? || Time.now > end_time

        raise TimeoutError, "  ->  #{@pid} still alive after #{timeout} seconds" unless exited?
      end

      def wait
        return if exited?

        _, @status = waitpid2(@pid)
      end

      private

      def terminate_and_wait_else_kill(timeout)
        WebDriver.logger.debug("Sending TERM to process: #{@pid}", id: :process)
        terminate(@pid)
        poll_for_exit(timeout)

        WebDriver.logger.debug("  -> stopped #{@pid}", id: :process)
      rescue TimeoutError, Errno::EINVAL
        WebDriver.logger.debug("    -> sending KILL to process: #{@pid}", id: :process)

View on GitHub (pinned to aa36b38e69)

Solutions

  1. Increase the timeout passed to poll_for_exit / the managed process stop call.
  2. Ensure signals (TERM/KILL) reach the child; in containers run with a proper init (tini) or share the PID namespace correctly.
  3. Reap orphaned processes from prior runs before starting new ones.
  4. Investigate why the child ignores shutdown — capture its logs before killing.

Example fix

// before
process.poll_for_exit(5)
// after
process.poll_for_exit(30)
Defensive patterns

Strategy: retry

Validate before calling

# No pre-check can force a child to exit; pick a timeout appropriate to the environment.
timeout = ci? ? 60 : 10

Try / catch

begin
  process.poll_for_exit(timeout)
rescue TimeoutError => e
  warn "child alive after #{timeout}s: #{e.message}"
  process.kill rescue nil
end

Prevention

When it happens

Trigger: Calling process.poll_for_exit(timeout) (typically via stop/wait on a managed process) where the child does not terminate within the timeout — e.g. an unresponsive driver or server process that ignores SIGTERM.

Common situations: Driver/browser/server process hung, OS slow to deliver signals, too-small timeout on a loaded CI box, zombie processes, container not forwarding signals.

Understand the failure class

Related errors


AI-assisted analysis of SeleniumHQ/selenium@aa36b38e69 (2026-08-14). Data as JSON: /api/errors/b7471e1cd0252fef. Report an issue: GitHub.