skip to content

A Selenium test on a translation-memory editor fails with a bare unknown error - how does ChromeDriver's own log find the real cause?

level: seniorimportance: should knowfreq 44%

answer

  1. The driver process keeps its own record
  2. A stack trace is one sentence of it
  3. Default detail hides the command traffic
  4. One flag picks the file, another the detail
  5. Command line, then its matching response

basics

~20 s

ChromeDriver's own log records every command the client sent, its full JSON payload, and the browser's real reply. Start the driver with --log-level=DEBUG and --log-path, then read the last command before the failure and its response.

solid answer

~40 s

A client stack trace shows only the exception the Selenium 4 binding threw; the driver's own log shows the whole conversation. Give ChromeDriver a destination with `--log-path=<file>` and raise the detail with `--log-level=DEBUG` (`--verbose` is the same as `--log-level=ALL`); from Java that is `new ChromeDriverService.Builder().withLogFile(file).withLogLevel(ChromiumDriverLogLevel.DEBUG).build()`, passed into the `ChromeDriver` constructor. Each request then appears as a command line naming the endpoint with its JSON payload, followed by its response, so you can see the locator that was really sent, whether the click reached the browser, and the remote end's own error text - which is longer and more specific than what the client surfaces. Add `--readable-timestamp` so entries carry a wall-clock time. geckodriver is the same idea with `--log trace` or `withLogLevel(FirefoxDriverLogLevel.TRACE)`.

code

java · 18 lines
java
import java.io.File;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.chromium.ChromiumDriverLogLevel;

public final class TranslationMemoryDriver {

  public static ChromeDriver open(File logFile) {
    ChromeDriverService service = new ChromeDriverService.Builder()
        .withLogFile(logFile)
        .withLogLevel(ChromiumDriverLogLevel.DEBUG)
        .withAppendLog(true)
        .withReadableTimestamp(true)
        .build();
    return new ChromeDriver(service, new ChromeOptions());
  }
}

go deeper

for a junior

Know that the driver is a separate program with a log of its own, and that Selenium can be told to write that log to a file. Being able to say the file exists is enough at this stage.

for a middle

Be ready to name the flags that produce the file and the detail in it, and to explain that each entry is a command with its payload followed by the browser's reply.

for a senior

Show that you reach for this log during the failing run rather than after it, and that you can read an exchange backwards from the failure to the command that caused it. Interviewers want the diagnostic habit, not the flag list.

for a principal

Own the standing decision: what detail level the suite runs at, where those files land, how long they are kept, and the fact that command payloads make them sensitive artefacts rather than throwaway noise.

## Two records of the same failure When a Selenium 4 test drives a **translation-memory editor** and a step blows up, two independent records of that moment exist. The **client stack trace** is produced by the language binding inside your test process: an exception type, a one-line message the driver handed back, and the Java frames that reached the call. The **driver log** is produced by the driver executable itself — `chromedriver` or `geckodriver` — the separate process that sits between your client and the browser. It records the request the client sent, the JSON body of that request, and the answer the browser actually gave. The stack trace is the last sentence of that conversation. The log is the conversation. That gap is exactly why a bare `unknown error` is unhelpful on its own. `unknown error` is the WebDriver error code the remote end uses when a failure does not fit a more specific code, and everything that would identify it — which command, which payload, which browser-level complaint — lives in the driver's own log. ## Turning the log on At its default level ChromeDriver records little more than startup and session lifecycle, so a log file on its own will show you nothing about the failing step. You need both a destination and a level: | Goal | ChromeDriver flag | Java, on `ChromeDriverService.Builder` | |---|---|---| | choose the file | `--log-path=<file>` | `withLogFile(File)` | | record each command | `--log-level=DEBUG` | `withLogLevel(ChromiumDriverLogLevel.DEBUG)` | | record everything | `--verbose`, equal to `--log-level=ALL` | `withVerbose(true)` | | keep the previous run | `--append-log` | `withAppendLog(true)` | | wall-clock times | `--readable-timestamp` | `withReadableTimestamp(true)` | geckodriver spells the same idea as `--log <level>`, choosing from `fatal`, `error`, `warn`, `info` (its default), `config`, `debug` and `trace`; from Java that is `GeckoDriverService.Builder.withLogLevel(FirefoxDriverLogLevel.TRACE)`. geckodriver has no log-path flag of its own, so `withLogFile(File)` or `withLogOutput(OutputStream)` on the builder is how you keep what it writes. ## Reading the failing exchange Once the file exists, the read is mechanical: 1. **Find the session.** Each line carries the id of the session it belongs to, so a file holding more than one run can be split by that id before you read anything. 2. **Go to the end of that session's lines.** The failure is the last thing that happened, and the driver stops issuing commands after it. 3. **Read the last command.** A request appears as a line naming the endpoint with its JSON payload — the locator, the text, the script, the element id the client sent. 4. **Read the matching response.** That is where the remote end's own words are: `no such element`, `element click intercepted`, `stale element reference`, a browser-side exception text. 5. **Walk backwards** through the preceding commands until the picture makes sense: what was found, what was clicked, what the previous step left the page in. Abbreviated, one command and its reply look like this: ```text [1727.318][INFO]: [4f2c] COMMAND FindElement { "using": "css selector", "value": "#segment-118 .tm-match button.accept" } [1727.842][INFO]: [4f2c] RESPONSE FindElement ERROR no such element: Unable to locate element ``` ## What that buys you in the editor - The **payload you actually sent** is visible, which catches the interpolated locator that was built from the wrong segment number. - The **remote end's own error text** is longer and more specific than the exception message the client surfaces. - The **element that intercepted a click** is named by the browser, so the fuzzy-match tooltip covering the accept button stops being a guess. - The **order and timing** of commands is visible, so a step that fired before the segment list finished rendering shows up as a find issued milliseconds after navigation. - A command that **succeeded** is just as informative: if the click was accepted and the segment still did not save, the fault is above the driver and no amount of driver logging will show it. ## Where the log stops - It is the **driver's** record, not the page's. Messages the application's own scripts printed are a different stream entirely and are not in this file. - It ends at the **browser boundary**. What the editor's backend did with a save request is in the server's logs, not here. - Its default timestamps are **elapsed time since that driver process started**, not a time of day, which is why `--readable-timestamp` matters as soon as you want to line an entry up against anything else. - Turning the level up is not free: at `DEBUG` and above, every command payload is written out, including text typed into the editor, so the file grows quickly and must be handled as sensitive material. ## The habit worth having Configure the log destination once, in the code that builds the driver, rather than reaching for it after a failure. A verbose log captured **during** the failing run answers the question; a log turned on afterwards only answers it if the failure repeats, and the failures that need this file are usually the ones that do not.

  • Where does the driver's log go if you never configure a file or an output stream?
    Nowhere you can read after the run. The driver executable writes to its own output streams, and unless the Selenium 4 service is given `withLogFile(File)` or `withLogOutput(OutputStream)` - or ChromeDriver is given `--log-path` directly - that output is not preserved. Configure the destination in the code that builds the driver rather than hoping to find it afterwards.
  • The log shows the click command returning success, yet the segment was never saved. What does that tell you?
    That the fault is above the driver. The browser accepted the click at the point the driver computed and reported success, so the command genuinely reached the page. The next evidence is the request the page made and what the editor's backend answered - the driver log stops at the browser boundary and will not show it.
  • Why does a DEBUG-level driver log grow so quickly during a long editing session?
    Because every command is written with its full JSON payload and its response, and a segment-by-segment run issues thousands of finds, clicks and key-sending calls. Text typed into the editor is logged verbatim, so a single large paste into a target segment can add kilobytes on its own. Budget disk for it and rotate.

A client stack trace is the note a courier leaves saying delivery failed; the driver log is the courier's own route record, showing which door was knocked on and what the person who answered actually said.

saying these in an interview costs you the question

  • Assumes the client stack trace already carries the remote end's real error
  • Believes ChromeDriver logs every command at its default level
  • Confuses the driver's own log with messages the page printed
  • Sets a log path but never raises the log level, then sees nothing
  • Reads only the exception class name and reruns hoping it passes