PRACTICAL GUIDE / Selenium BiDi console logs
Capture Console Messages and JavaScript Errors with Selenium BiDi
Capture Selenium BiDi console messages and JavaScript exceptions with scoped listeners, thread-safe evidence, filtering policy, and failure diagnostics.
In this guide11 sections
- Separate Console Policy from JavaScript Failure Policy
- Enable BiDi and Register Before Navigation
- Wait for Product Completion Before Judging Evidence
- Filter with an Explicit Product Policy
- Use Source and Stack Data for Diagnosis
- Handle Multiple Tabs and Frames Deliberately
- Control Volume and Memory
- Diagnose Missing or Duplicate Events
- Balance Assertion Value and Test Coupling
- Console Evidence Checklist
- Turn Browser Noise into a Defensible Signal
What you will learn
- Separate Console Policy from JavaScript Failure Policy
- Enable BiDi and Register Before Navigation
- Wait for Product Completion Before Judging Evidence
- Filter with an Explicit Product Policy
Browser console evidence is useful only when the test knows what it means. An uncaught JavaScript exception can explain a frozen checkout, while a deliberate console.error may be an application's handled diagnostic. Selenium BiDi exposes both as live events, but the test still needs scope, timing, classification, and a completion boundary.
The reliable pattern is simple: enable BiDi at session creation, register listeners before the event can occur, collect into thread-safe structures, wait for the product workflow to finish, then apply a documented policy. Do not read "whatever logs exist at teardown" and call every entry a product failure.
Separate Console Policy from JavaScript Failure Policy
Console entries carry level, text, source, timestamp, and console-specific fields. JavaScript exception entries represent script failures and may include stack information. A product can emit the same text through either path, but their semantics differ. Keep two collections and two decisions.
The Selenium BiDi logging documentation exposes console-message handlers and JavaScript-error handlers. Java's LogInspector offers typed ConsoleLogEntry and JavascriptLogEntry callbacks over the negotiated BiDi session.
Animated field map
BiDi Browser Error Evidence
Page scripts emit browser events; scoped BiDi listeners collect them, policy filters expected noise, and the test attaches only relevant assertion evidence.
01 / page script
Page Script
Application code logs a message or throws during the tested workflow.
02 / browser log event
Browser Log Event
The browser emits typed console or JavaScript exception data.
03 / bidi subscription
BiDi Subscription
A context-scoped LogInspector registers before navigation or interaction.
04 / evidence filter
Severity and Context Filter
Product policy classifies unexpected errors without hiding raw evidence.
05 / test assertion
Test Evidence Assertion
The completed workflow fails on relevant entries and attaches a concise report.
Enable BiDi and Register Before Navigation
Request webSocketUrl in the options before creating the driver. Capture the initial window handle, create a context-scoped inspector, and add both handlers before navigating to a page whose startup scripts may fail. CopyOnWriteArrayList is suitable for a modest diagnostic stream with many reads and few writes; a bounded queue is better for high-volume logging.
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.bidi.log.ConsoleLogEntry;
import org.openqa.selenium.bidi.log.JavascriptLogEntry;
import org.openqa.selenium.bidi.module.LogInspector;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
ChromeOptions options = new ChromeOptions();
options.setCapability("webSocketUrl", true);
WebDriver driver = new ChromeDriver(options);
List<ConsoleLogEntry> consoleEntries = new CopyOnWriteArrayList<>();
List<JavascriptLogEntry> scriptErrors = new CopyOnWriteArrayList<>();
try (LogInspector logs = new LogInspector(driver.getWindowHandle(), driver)) {
logs.onConsoleEntry(consoleEntries::add);
logs.onJavaScriptException(scriptErrors::add);
driver.get("https://app.example.test/checkout");
runCheckoutScenario(driver);
assertBrowserEvidence(consoleEntries, scriptErrors);
} finally {
driver.quit();
}Creating the inspector after get() would miss errors from parsing, module loading, and application bootstrap. Creating it globally for an entire shared browser would mix tests. The inspector's try-with-resources scope should match the workflow whose logs will be judged.
Wait for Product Completion Before Judging Evidence
Events arrive asynchronously, so an empty list immediately after click() proves little. Wait for a stable product boundary such as a submitted order state, rendered error result, or completed route transition. That boundary says the workflow had the opportunity to emit its events.
When the expected result is itself a specific console or exception event, wait on the thread-safe collection with WebDriverWait or poll the queue with a bound. Do not add a fixed sleep "for logs to settle." A sleep either delays every passing test or still races a slower event.
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(12));
wait.until(ExpectedConditions.attributeToBe(
By.cssSelector("[data-testid='checkout-state']"),
"data-state",
"complete"));
wait.until(ignored -> consoleEntries.stream()
.anyMatch(entry -> entry.getText().contains("checkout-complete")));The marker in this example is an application-owned diagnostic contract. Avoid asserting arbitrary third-party wording. If no log event is part of the expected behavior, wait only for product completion and then classify whatever evidence was captured.
Filter with an Explicit Product Policy
Start by separating JavaScript exceptions from console levels. Unexpected uncaught exceptions in the tested context are usually failures. Console ERROR entries may fail unless a named dependency and known scenario are allowed. WARNING, INFO, and DEBUG entries are generally diagnostic unless the test specifically validates them.
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.stream.Collectors;
import org.openqa.selenium.bidi.log.LogLevel;
List<ConsoleLogEntry> unexpectedConsoleErrors = consoleEntries.stream()
.filter(entry -> entry.getLevel() == LogLevel.ERROR)
.filter(entry -> !knownConsolePolicy.allows(entry))
.toList();
String consoleSummary = unexpectedConsoleErrors.stream()
.map(entry -> entry.getLevel() + ": " + entry.getText())
.collect(Collectors.joining(System.lineSeparator()));
assertTrue(scriptErrors.isEmpty(), () -> summarizeScriptErrors(scriptErrors));
assertTrue(unexpectedConsoleErrors.isEmpty(), () -> consoleSummary);An allowlist needs an owner, reason, narrow fingerprint, and removal condition. Do not allow every message containing "failed" or every event from a broad hostname. Preserve the raw entry in restricted artifacts even when policy allows it, so dependency changes can be reviewed without weakening the test silently.
Use Source and Stack Data for Diagnosis
Text alone collapses unrelated failures. Include log level, entry type, source context, timestamp, and stack frames when available. Normalize volatile URL query values before grouping, but keep enough path and line information to locate the failing bundle. Attach the current route and test step separately rather than concatenating everything into one assertion message.
Console arguments can include structured remote values. Treat them as untrusted and potentially sensitive. Prefer the browser-provided text for routine reports, and serialize arguments only through a bounded redaction function. Never dump access tokens, personal data, or entire application state because an error callback made them convenient.
Handle Multiple Tabs and Frames Deliberately
A top-level browsing-context subscription keeps unrelated tabs out of the collection. When the workflow opens a popup, capture its handle and create a second inspector scoped to that handle before triggering popup-side behavior. Merge evidence only if the test's policy says both contexts belong to one scenario.
Frames have their own execution contexts even when they contribute to one top-level user flow. Record the source context carried by the event and avoid assuming every script error came from the main document. Third-party frame errors may be diagnostic, product failures, or excluded by contract; decide that in policy, not by deleting all frame-originated entries.
Control Volume and Memory
Applications can emit logs in loops. An unbounded list can consume memory and produce unusable artifacts. Set a maximum entry count or byte budget, retain the first relevant errors and a tail sample, and record how many events were omitted. Overflow should make the evidence status visible.
Keep the handler tiny. Redaction that needs parsing, source maps, or artifact upload should happen after collection. A slow callback delays processing of later events and can make the apparent order diverge from the browser's event stream.
Diagnose Missing or Duplicate Events
No events at all suggests that BiDi was not negotiated, the inspector was created too late, the browser or remote environment lacks the event support, or the wrong context was supplied. Events from a manual console call but not from the application point to trigger timing or execution context. Duplicate entries usually mean duplicate inspectors or a listener that survived a retry.
If the application visibly breaks but no JavaScript exception arrives, check whether the code caught the exception and logged it instead. Also inspect failed network requests and product error state. BiDi logging is one evidence channel, not a universal detector for every client-side defect.
Balance Assertion Value and Test Coupling
Failing every end-to-end test on every console message creates broad coupling to analytics, browser extensions, and third-party scripts. Limiting checks to a few critical workflows reduces noise but leaves coverage gaps. A practical design uses strict JavaScript-exception checks on product-owned contexts, targeted console policy on critical paths, and a separate observational sweep for ecosystem noise.
Keep functional assertions primary. A successful payment should be judged by product and backend state; a clean console is supporting evidence. Conversely, do not let a green UI assertion erase a captured uncaught exception that indicates hidden corruption after the visible step.
Console Evidence Checklist
- Enable
webSocketUrlbefore session creation. - Register console and JavaScript handlers before navigation or action.
- Scope the inspector to the browsing context under test.
- Store callbacks in thread-safe, bounded structures.
- Wait for a product completion boundary before classification.
- Keep exceptions and console messages as distinct evidence types.
- Apply a narrow, owned allowlist rather than broad text suppression.
- Include source and stack data without leaking sensitive values.
- Close the inspector before reusing a browser or retrying a test.
- Report missing, dropped, and allowed evidence explicitly.
Turn Browser Noise into a Defensible Signal
BiDi log capture is strongest when the test can explain every step: which context was observed, when handlers became active, what completed the workflow, and why each entry passed or failed policy. Capture console and exception streams separately, preserve their typed evidence, and keep the allowlist narrow. Then a browser error is not background text; it is a traceable product finding tied to the exact scenario that exposed it.
// LIVE COURSE / THE TESTING ACADEMY
Playwright Automation Mastery
Go beyond Selenium. Master Playwright with JS/TS in 90 days.
From the instructor behind this guide.
Playwright jobs are growing 8x faster than Selenium. 90 days / 75+ live hrs / Tue-Thu-Sat 7 AM IST.
PRIMARY REFERENCES
Verify the details at the source
QABattle guides are practical explanations. Product behavior, standards, and APIs can change, so use these primary references for the canonical details.
- 01Selenium documentation
Selenium Project
Canonical WebDriver, Grid, waits, element, and browser automation guidance.
- 02WebDriver standard
W3C
The browser automation protocol specification behind WebDriver implementations.
FAQ / QUICK ANSWERS
Questions testers ask
Are console.error messages the same as uncaught JavaScript exceptions?
No. Application code can call console.error without throwing, while an uncaught exception is a JavaScript error event. Capture and classify both streams instead of treating them as interchangeable.
When should a Selenium test subscribe to BiDi log events?
Subscribe before navigation or before the interaction that may emit the event. BiDi does not replay earlier events to a handler registered after the fact.
Should every browser console warning fail the test?
Usually not. Define a product-owned policy by severity, source, context, and known fingerprint. Fail on unexpected high-risk entries while retaining lower-severity messages as diagnostics.
Why use a thread-safe collection for log entries?
BiDi handlers receive events independently of the test command thread. A concurrent collection or queue prevents races while the test waits for completion and evaluates captured evidence.
How do I avoid logs from an unrelated tab?
Construct the Selenium Java LogInspector with the intended browsing-context ID, register before the trigger, and create a separate scoped inspector for a popup or newly opened tab.
RELATED GUIDES
Continue the learning route
GUIDE 01
Enable WebDriver BiDi and Manage Event Subscriptions in Selenium
Enable Selenium WebDriver BiDi, verify WebSocket negotiation, scope event subscriptions, prevent races, and clean handlers up deterministically.
GUIDE 02
Instrument Selenium Commands with WebDriver Listeners
Instrument Selenium commands with WebDriverListener, structured timing events, failure screenshots, safe redaction, and useful execution timelines.
GUIDE 03
Intercept Requests and Supply Authentication with Selenium BiDi Network
Use Selenium BiDi network intercepts for scoped Basic or Digest authentication, request control, response evidence, cleanup, and safe diagnostics.
GUIDE 04
Selenium BiDi Script Realms and Preload Scripts for Test Instrumentation
Use Selenium BiDi script realms and preload scripts for isolated instrumentation, test-side channels, typed evaluation, cleanup, and failure analysis.