A suite that passed reliably on laptops moved to a Grid and became unreliable. No test changed. The team suspected the Grid was broken.
The Grid was fine. Distributed execution changes four things, and each breaks a different assumption.
One: latency multiplies.
Every command is an HTTP round trip, as the protocol lecture explained. Locally that is a millisecond. To a Grid in another data centre it might be 30 to 50ms.
A test making 100 commands goes from 0.1 seconds of protocol overhead to 5 seconds. A suite of 2,000 such tests adds hours.
What to do about it. Reduce chattiness rather than accepting the cost. Find an element once and reuse the reference instead of re-finding it. Use executeScript to gather several values in one round trip where you would otherwise make ten calls. Set polling intervals higher, because each poll is a round trip.
That last point is the one people miss: a wait polling every 100ms across a suite generates enormous traffic against a remote Grid.
Two: the browser is not on your machine.
No screen to watch, no console to open, no way to pause and inspect. Whatever you did not record did not happen, and this is the biggest practical change.
Collect evidence unconditionally. Screenshot on failure, browser console log, page source, and the session ID. The session ID matters more than people expect: cloud providers and Grid consoles let you look up a session's video and logs by ID, and without it in your report you cannot find the recording.
Three: local assumptions break.
File paths. sendKeys with a local path fails, because the file is not on the node. Selenium's LocalFileDetector uploads the file to the node first, and this is the standard fix that nobody discovers on their own:
const remote = require('selenium-webdriver/remote');
driver.setFileDetector(new remote.FileDetector());
Downloads land on the node, not on your machine. Cloud providers offer an API to retrieve them; on a self-hosted Grid you need a shared volume or you check the export endpoint over HTTP instead.
Localhost is not your localhost. A node pointed at http://localhost:3000 looks for a server on the node. Your application must be reachable from the node's network, which usually means a hostname rather than localhost, and this catches out everyone the first time.
Time zone and locale come from the node, not from you. Tests asserting on formatted dates will differ. Set these explicitly in node configuration rather than assuming.
Four: capacity becomes a shared resource.
Your suite is not alone. Another team's run can exhaust the Grid, and your tests then wait in the queue or fail to get a session at all. That failure looks nothing like a test problem and everything like flakiness.
Handle session creation failure explicitly. A retry with backoff around driver creation turns "no available node right now" into a short wait rather than a failed run. Distinguish it in your logs from a test failure, because they need different responses.
Debugging a distributed failure. Work in this order, because it eliminates whole categories fast:
Did the session start? No means capacity or configuration, not your test. Did it fail on one node repeatedly? That node is unhealthy. Check its memory and browser version. Does it fail on the Grid and pass locally, consistently? Latency, timezone, or an unreachable application URL. Does it fail on the Grid intermittently? Contention. Check queue depth and session duration during the run.
Working through that list is faster than reading the test, and most people read the test first.
Remember this: latency multiplies so reduce chattiness, record everything because you cannot watch, and check whether the session started before blaming the test.
Reference: Remote WebDriver.