jhy/jsoup · error · SocketTimeoutException
Read timeout
Error message
Read timeout
What it means
ControllableInputStream.read wraps the underlying stream with an overall deadline and optional byte cap. Before each underlying read it calls expired(); if the total elapsed time exceeds the configured timeout it throws SocketTimeoutException('Read timeout'). This means the connection produced no usable data within the overall time budget, even if individual reads were trickling in.
Solutions
- Raise the timeout: Jsoup.connect(url).timeout(30000) or higher for slow hosts.
- Verify the URL/host responds (curl --max-time) to distinguish slowness from outage.
- Retry with backoff for transient slowness.
- Check proxy/VPN and network path health if all targets time out.
- Use Response with maxBytes only when needed — large caps on slow links need proportionally larger timeouts.
Example fix
// before
Document doc = Jsoup.connect(url).timeout(1000).get();
// after
Document doc = Jsoup.connect(url)
.timeout(30000)
.get(); Defensive patterns
Strategy: retry
Validate before calling
long timeoutMs = connection.request().timeout(); if (timeoutMs < 5000 && isSlowHost(url)) raise timeout before connecting;
Try / catch
try { doc = Jsoup.connect(url).timeout(30000).get(); } catch (SocketTimeoutException e) { /* retry with backoff or fail gracefully */ } Prevention
- Set timeouts proportionate to expected response size and link speed.
- Implement retry-with-backoff for flaky hosts.
- Avoid tiny maxBytes caps on slow connections.
- Test flaky endpoints with curl --max-time to baseline realistic timeouts.
When it happens
Trigger: Jsoup.connect(url).timeout(ms).execute()/parse() where the server is slow or stalls; reads keep returning small chunks so total elapsed time exceeds the timeout; .maxBytes caps combined with slow networks; a dead proxy accepting connections but never sending data.
Common situations: Fetching slow or overloaded sites; scraping through a degraded proxy or VPN; long-tail responses on mobile networks; timeout set too low for large downloads on slow links.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
AI-assisted analysis of jhy/jsoup@9851ac5d9c (2026-09-08).
Data as JSON: /api/errors/cb321a9c94e3176b.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/org/jsoup/internal/ControllableInputStream.java:106
// interrupted latches, because parse() may call twice
interrupted = true;
return -1;
}
if (capped && remaining <= 0) {
if (checkTruncated()) return -1;
contentLength = readPos;
emitProgress();
return -1;
}
if (capped && len > remaining)
len = remaining; // don't read more than desired, even if available
if (capped) buff.capRemaining(remaining);
else buff.uncap();
while (true) { // loop trying to read until we get some data or hit the overall timeout, if we have one
if (expired())
throw new SocketTimeoutException("Read timeout");
try {
final int read = super.read(b, off, len);
if (read == -1) { // completed
contentLength = readPos;
} else {
if (capped && read > 0) {
remaining -= read; // track bytes returned to the caller
}
// todo: use long progress values in the public API; saturate until that is available
readPos = read > Integer.MAX_VALUE - readPos ? Integer.MAX_VALUE : readPos + read;
}
emitProgress();
return read;
} catch (SocketTimeoutException e) {
if (expired() || timeout == 0)
throw e;
}View on GitHub (pinned to 9851ac5d9c)