BrowserStack timeouts are the #1 source of CI/CD failures for teams running remote device testing. A passing test locally becomes a flaky timeout nightmare in the cloud.
This guide covers every timeout type, explains why each occurs, and provides working code fixes.
Understanding BrowserStack Timeouts
BrowserStack has four main timeout types:
| Timeout | Default | Maximum | Trigger |
|---|---|---|---|
| BROWSERSTACK_IDLE_TIMEOUT | 90 seconds | 300 seconds | No command received |
| SO_TIMEOUT | 240 seconds | N/A | Browser unresponsive |
| Session Limit | 2 hours | 2 hours | Max session duration |
| Network Idle | 30 seconds | Configurable | Asset discovery (Percy) |
Let’s debug each one.
1. BROWSERSTACK_IDLE_TIMEOUT
This is the most common error:
[BROWSERSTACK_IDLE_TIMEOUT] Session timed out due to inactivity for configured time interval (default: 90 secs)
Why It Happens
BrowserStack doesn’t know when your test script finishes. It waits for the next command. If no command arrives within 90 seconds, it assumes something went wrong and kills the session.
Root causes:
- Missing driver.quit() — Test ends without telling BrowserStack
- Unhandled exception — Test crashes before reaching driver.quit()
- Long-running operation — A command takes longer than 90 seconds
- sleep() calls — Waiting longer than 90 seconds between commands
Fix 1: Always Call driver.quit()
// ❌ Bad: driver.quit() might not execute if test fails
@Test
public void testLogin() {
driver.findElement(By.id("username")).sendKeys("user");
driver.findElement(By.id("password")).sendKeys("pass");
driver.findElement(By.id("submit")).click();
assertEquals("Welcome", driver.findElement(By.id("greeting")).getText());
driver.quit(); // Never reached if assertion fails
}
// ✅ Good: Use try-finally to guarantee quit()
@Test
public void testLogin() {
try {
driver.findElement(By.id("username")).sendKeys("user");
driver.findElement(By.id("password")).sendKeys("pass");
driver.findElement(By.id("submit")).click();
assertEquals("Welcome", driver.findElement(By.id("greeting")).getText());
} finally {
driver.quit(); // Always executes
}
}
For test frameworks, use hooks:
// JUnit 5
@AfterEach
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
# pytest
@pytest.fixture
def driver():
driver = webdriver.Remote(...)
yield driver
driver.quit() # Cleanup guaranteed
Fix 2: Increase Idle Timeout
If your tests legitimately need longer waits:
// Selenium (Java)
DesiredCapabilities caps = new DesiredCapabilities();
caps.setCapability("browserstack.idleTimeout", 300); // Max 300 seconds
# Appium (Python)
desired_caps = {
'browserstack.idleTimeout': 300,
'newCommandTimeout': 300 # Appium-specific
}
// WebdriverIO
capabilities: {
'bstack:options': {
idleTimeout: 300
}
}
Fix 3: Handle Exceptions
Wrap risky operations:
from selenium.common.exceptions import NoSuchElementException, TimeoutException
def safe_click(driver, locator):
try:
element = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable(locator)
)
element.click()
except (NoSuchElementException, TimeoutException) as e:
print(f"Element not found: {locator}")
raise # Re-raise after logging, still hits finally block
Fix 4: Replace sleep() with Explicit Waits
# ❌ Bad: Hard sleep causes idle timeout if >90s
time.sleep(120)
# ✅ Good: Explicit wait sends polling commands
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
WebDriverWait(driver, 120).until(
EC.visibility_of_element_located((By.ID, "result"))
)
Explicit waits poll the element, sending commands to BrowserStack and resetting the idle timer.
2. SO_TIMEOUT (Socket Timeout)
SO_TIMEOUT: The browser became unresponsive for 240 seconds
Why It Happens
The browser process on BrowserStack’s device stopped responding. Common causes:
- Unhandled popup blocking execution
- Browser crash
- Heavy JavaScript causing hang
- Memory exhaustion
Fix: Handle Popups and Alerts
// Check for and dismiss alerts
try {
Alert alert = driver.switchTo().alert();
alert.dismiss();
} catch (NoAlertPresentException e) {
// No alert, continue
}
// Handle permission dialogs (Appium)
driver.switchTo().alert().accept();
Fix: Monitor Memory Usage
If tests consistently hit SO_TIMEOUT, the app might be leaking memory:
# Restart app periodically in long test sessions
if test_count % 20 == 0:
driver.terminate_app(app_id)
driver.activate_app(app_id)
3. Session Limit Reached
SESSION_LIMIT_REACHED: Session exceeded maximum allowed duration of 2 hours
Why It Happens
BrowserStack enforces a 2-hour (7200 second) maximum session length. This is a hard limit.
Fix: Split Long Test Suites
Instead of one 3-hour session:
# ❌ Bad: Single session for all tests
def run_all_tests():
driver = create_driver()
for test in all_tests: # 300 tests, 3+ hours
test(driver)
driver.quit()
Run multiple shorter sessions:
# ✅ Good: Chunked sessions
def run_tests_chunked(tests, chunk_size=50):
for chunk in chunks(tests, chunk_size):
driver = create_driver()
for test in chunk:
test(driver)
driver.quit() # New session for next chunk
Fix: Use Test Sharding
Distribute tests across parallel sessions:
# GitHub Actions matrix
jobs:
test:
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- run: pytest tests/ --shard=${{ matrix.shard }}/4
For more optimization strategies, see Mobile Testing CI/CD Pipeline Optimization.
4. BrowserStack Local Connection Issues
BrowserStack Local creates a tunnel between your network and BrowserStack’s cloud. It’s notoriously flaky. For a dedicated guide on Local issues, see BrowserStack Local Dropping: 2025 Fix.
Common Errors
[browserstack.local] is set to true but local testing through BrowserStack is not connected
BrowserStack Local connection dropped
Fix 1: Wait for Tunnel Establishment
Don’t run tests until the tunnel is ready:
# In CI script
./BrowserStackLocal --key $BROWSERSTACK_ACCESS_KEY &
sleep 5 # Wait for connection
# Or better, check for success message
./BrowserStackLocal --key $BROWSERSTACK_ACCESS_KEY 2>&1 | while read line; do
if [[ "$line" == *"[SUCCESS]"* ]]; then
echo "Tunnel ready"
break
fi
done
Fix 2: Use Unique Local Identifiers
When running parallel CI jobs, each needs a unique identifier:
import uuid
local_identifier = f"ci-{os.environ.get('CI_JOB_ID', uuid.uuid4())}"
caps = {
'browserstack.local': True,
'browserstack.localIdentifier': local_identifier
}
# Start binary with matching identifier
./BrowserStackLocal --key $KEY --local-identifier $CI_JOB_ID
Fix 3: Handle Proxy Environments
Corporate networks often require proxy configuration:
# With proxy
./BrowserStackLocal --key $KEY \
--proxy-host proxy.company.com \
--proxy-port 8080 \
--proxy-user $PROXY_USER \
--proxy-pass $PROXY_PASS
# With PAC file
./BrowserStackLocal --key $KEY --pac-file /path/to/proxy.pac
Fix 4: Force Local Routing
Ensure all traffic goes through the tunnel:
./BrowserStackLocal --key $KEY --force-local
This routes ALL requests through your machine, useful when your app only accepts traffic from known IPs.
XCUI Test Specific Timeouts
For XCUITest on BrowserStack App Automate:
curl -u "USER:KEY" \
-X POST "https://api-cloud.browserstack.com/app-automate/xcuitest/v2/build" \
-d '{
"idleTimeout": 180,
"devices": ["iPhone 15-17"],
"app": "bs://app-id",
"testSuite": "bs://suite-id"
}' \
-H "Content-Type: application/json"
Default idle timeout for XCUI is 900 seconds (15 minutes), but session max is still 2 hours.
Debugging Timeout Issues
When timeouts occur, gather this information:
1. Check Session Video
BrowserStack records every session. Watch what happened:
https://automate.browserstack.com/builds/<build-id>/sessions/<session-id>
2. Review Text Logs
Look for the last successful command:
2024-01-14 10:15:32 POST /session/abc123/element {"using":"id","value":"submit"}
2024-01-14 10:15:33 Response: {"value":{"element-6066-...":"xyz"}}
2024-01-14 10:15:33 POST /session/abc123/element/xyz/click
2024-01-14 10:17:03 [BROWSERSTACK_IDLE_TIMEOUT] Session timed out
This shows the click happened, then 90 seconds of nothing.
3. Check Network Logs
If using BrowserStack Local, verify the tunnel stayed connected:
[INFO] Connected to BrowserStack
[INFO] Press Ctrl-C to exit
...
[WARN] Connection interrupted, reconnecting...
[ERROR] Failed to reconnect after 3 attempts
Configuration Reference
Selenium Capabilities
MutableCapabilities caps = new MutableCapabilities();
caps.setCapability("browserstack.idleTimeout", 300);
caps.setCapability("browserstack.local", true);
caps.setCapability("browserstack.localIdentifier", "unique-id");
caps.setCapability("browserstack.networkLogs", true); // Enable for debugging
Appium Capabilities
desired_caps = {
'platformName': 'Android',
'browserstack.idleTimeout': 300,
'newCommandTimeout': 300,
'browserstack.local': True,
'browserstack.localIdentifier': 'unique-id',
'browserstack.appiumLogs': True
}
BrowserStack SDK (browserstack.yml)
userName: YOUR_USERNAME
accessKey: YOUR_ACCESS_KEY
local: true
localIdentifier: ${CI_JOB_ID}
idleTimeout: 300
platforms:
- deviceName: iPhone 15
osVersion: 17
platformName: ios
Checklist: Preventing Timeouts
Before running tests on BrowserStack:
-
driver.quit()is in a finally block or test teardown - Exceptions are handled appropriately
- No
sleep()calls longer than 90 seconds - Explicit waits used instead of implicit waits where possible
-
idleTimeoutincreased if tests need longer waits - BrowserStack Local started before tests and success confirmed
- Unique
localIdentifierfor parallel CI jobs - Test suite can complete within 2 hours per session
- Alerts and popups are handled in test code
When to Contact Support
If you’ve tried everything and still see timeouts:
- Gather evidence: Session IDs, videos, logs
- Check status page: https://status.browserstack.com
- Contact support: [email protected]
Include:
- Session ID
- Timestamp of failure
- What you expected vs what happened
- Steps you’ve already tried
BrowserStack support is generally responsive, especially for consistent reproducible issues.
Consider Alternatives
If timeout issues persist, you may want to evaluate whether cloud testing is the right fit for your team — especially when you factor in BrowserStack’s hidden costs beyond the sticker price. Some options:
- BrowserStack Alternative — Running tests on your own devices eliminates network latency and tunnel issues
- Private Device Labs — Zero network hops mean zero timeout issues from infrastructure
- Hybrid approach — Use cloud for breadth, local devices for reliability-critical tests
Bottom Line
BrowserStack timeouts usually come down to three things:
- Missing driver.quit() — Always use try-finally
- Long waits without commands — Use explicit waits, increase idleTimeout
- Local tunnel instability — Wait for success, use unique identifiers
Fix these, and your flaky cloud tests become reliable.