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

  1. Fix the URL string so it includes a host, e.g. 'http://example.com/path' instead of 'http:///path'
  2. Validate the URL in your own code with new URI(s) or new URL(s) and check getHost() != null before handing it to jsoup
  3. If the value may be relative, resolve it against a known valid base URL before use
  4. 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

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


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)