jhy/jsoup · error · MalformedURLException
Only http & https protocols supported
Error message
Only http & https protocols supported
What it means
During execute(), jsoup checks the target URL's protocol and throws MalformedURLException if it is not http or https. The connection executor only implements HTTP(S); other schemes like file:, ftp:, or ws: cannot be executed.
Solutions
- Use only http:// or https:// URLs with Jsoup
- For local files use Jsoup.parse(File, charset) instead of connect()
- Validate protocol before executing: new URL(u).getProtocol() in ("http","https")
Example fix
// before
Document doc = Jsoup.connect("file:///var/www/page.html").get();
// after
Document doc = Jsoup.parse(new File("/var/www/page.html"), "utf-8"); Defensive patterns
Strategy: validation
Validate before calling
String proto = new URL(target).getProtocol(); if (!proto.equals("http") && !proto.equals("https")) throw new IllegalArgumentException("Unsupported protocol: " + proto); Type guard
boolean isHttpUrl(URL u) { return u.getProtocol().equals("http") || u.getProtocol().equals("https"); } Try / catch
try { return Jsoup.connect(url).execute(); } catch (MalformedURLException e) { /* non-HTTP scheme: route to file/FTP handler */ } Prevention
- Sanitize URLs from crawls/user input to http(s) only
- Use Jsoup.parse(File, charset) for local files
- Reject mailto:, ftp:, file: schemes at the crawler boundary
When it happens
Trigger: Jsoup.connect("file:///etc/hosts").execute(); calling url(new URL("ftp://...")) then execute(); user-supplied URLs carrying non-HTTP schemes.
Common situations: Trying to scrape local files via file:// (should use Jsoup.parse(new File(...), charset)); websockets or other protocols mistakenly routed through Jsoup; dynamic URLs from crawls that preserve ftp/mailto links.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
- The supplied URL, ' ', is malformed. Make sure it is an…
- You must execute the request before getting a response.
- URL not set. Make sure to call #url(...) before executing…
- Cannot follow redirect with a streamed request body…
- HTTP error fetching URL
AI-assisted analysis of jhy/jsoup@9851ac5d9c (2026-09-08).
Data as JSON: /api/errors/0615ad0f4fcb7be6.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/org/jsoup/helper/HttpConnection.java:910
Tests if two URLs share an HTTP origin, as defined by scheme, host, and effective port.
See <a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-4.3.1">RFC 9110, Section 4.3.1</a>.
*/
static boolean sameOrigin(URL first, URL second) {
int firstPort = first.getPort() != -1 ? first.getPort() : first.getDefaultPort();
int secondPort = second.getPort() != -1 ? second.getPort() : second.getDefaultPort();
return first.getProtocol().equalsIgnoreCase(second.getProtocol())
&& first.getHost().equalsIgnoreCase(second.getHost())
&& firstPort == secondPort;
}
static Response execute(HttpConnection.Request req, @Nullable Response prevRes) throws IOException {
Validate.isTrue(req.executing.tryLock(), "Multiple threads were detected trying to execute the same request concurrently. Make sure to use Connection#newRequest() and do not share an executing request between threads.");
Validate.notNullParam(req, "req");
URL url = req.url();
Validate.notNull(url, "URL must be specified to connect");
String protocol = url.getProtocol();
if (!protocol.equals("http") && !protocol.equals("https"))
throw new MalformedURLException("Only http & https protocols supported");
final boolean supportsBody = req.method().hasBody();
final boolean hasBody = req.body != null;
if (!supportsBody)
Validate.isFalse(hasBody, "Cannot set a request body for HTTP method " + req.method());
// set up the request for execution
if (!req.data().isEmpty() && (!supportsBody || hasBody))
serialiseRequestUrl(req);
else if (supportsBody)
setOutputContentType(req);
long startTime = System.nanoTime();
RequestExecutor executor = RequestDispatch.get(req, prevRes);
Response res = null;
try {
res = executor.execute();
Method nextMethod = redirectMethod(res.statusCode, req.method());View on GitHub (pinned to 9851ac5d9c)