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

  1. Use only http:// or https:// URLs with Jsoup
  2. For local files use Jsoup.parse(File, charset) instead of connect()
  3. 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

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


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)