jhy/jsoup · error · MalformedURLException
Invalid URL: host is missing
Error message
Invalid URL: host is missing
What it means
jsoup validates that URLs with an http or https scheme include a non-empty host. Because a relative or malformed URL string like 'http://' has no host, StringUtil.resolve rejects it with a MalformedURLException rather than producing a useless base-less URL object.
Solutions
- Fix the URL string so it includes a host, e.g. 'http://example.com/path' instead of 'http:///path'
- Validate the URL in your own code with new URI(s) or new URL(s) and check getHost() != null before handing it to jsoup
- If the value may be relative, resolve it against a known valid base URL before use
- Catch MalformedURLException and surface a user-facing 'enter a complete URL including domain' message
Example fix
// before
String url = "http://";
Document doc = Jsoup.connect(url).get(); // MalformedURLException
// after
String url = "http://example.com";
if (new URL(url).getHost().isEmpty()) throw new IllegalArgumentException("URL needs a host");
Document doc = Jsoup.connect(url).get(); Defensive patterns
Strategy: validation
Validate before calling
import java.net.URL;
boolean hasHost(String s) throws MalformedURLException { return new URL(s).getHost() != null && !new URL(s).getHost().isEmpty(); } Try / catch
try { Document doc = Jsoup.connect(url).get(); } catch (MalformedURLException e) { throw new IllegalArgumentException("Provide a full URL with a host, e.g. http://example.com", e); } Prevention
- Validate URLs with java.net.URI/URL before use
- Always include scheme and host when building URLs from strings
- Resolve relative links against a known-good base URI
- Never concatenate user input into URL strings without validation
When it happens
Trigger: Calling jsoup URL resolution (e.g. StringUtil.resolve / URL resolution during link handling) with a URL string whose scheme is http/https but which lacks a host, such as 'http://', 'https:///path', or a base URL typed without the domain.
Common situations: Users entering incomplete URLs in forms or config files (missing domain), string concatenation that drops the host, or trimming/mangling a base URI before parsing.
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…
- Only http & https protocols supported
- Cannot follow redirect with a streamed request body…
AI-assisted analysis of jhy/jsoup@9851ac5d9c (2026-09-08).
Data as JSON: /api/errors/4eb0e5d693d31789.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/org/jsoup/internal/StringUtil.java:354
} catch (MalformedURLException e) {
try {
// the base is unsuitable, or resolution failed; the attribute/rel may be abs on its own
URL abs = new URL(relUrl);
validateHttpUrl(abs);
return abs.toExternalForm();
} catch (MalformedURLException ignored) {
// it may still be valid, just that Java doesn't have a registered stream handler for it, e.g. tel
// we test here vs at start to normalize supported URLs (e.g. HTTP -> http)
return validUriScheme.matcher(relUrl).find() && !hasHttpScheme(relUrl) ? relUrl : "";
}
}
}
private static final Pattern validUriScheme = Pattern.compile("^[a-zA-Z][a-zA-Z0-9+-.]*:");
/** Validates that an HTTP(S) URL has the host required by its scheme. */
private static void validateHttpUrl(URL url) throws MalformedURLException {
if (isHttpScheme(url.getProtocol()) && url.getHost().isEmpty())
throw new MalformedURLException("Invalid URL: host is missing");
}
/** Tests if the supplied scheme is HTTP or HTTPS. */
public static boolean isHttpScheme(String scheme) {
return scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https");
}
/** Tests if the supplied value starts with an HTTP or HTTPS scheme. */
public static boolean hasHttpScheme(String value) {
int colon = value.indexOf(':');
return colon > 0 && isHttpScheme(value.substring(0, colon));
}
private static final Pattern controlChars = Pattern.compile("[\\x00-\\x1f]*"); // matches ascii 0 - 31, to strip from url
private static String stripControlChars(final String input) {
return controlChars.matcher(input).replaceAll("");
}
View on GitHub (pinned to 9851ac5d9c)