{"record":{"id":"2220c1fdd5f7b601","repo":"conductor-oss/conductor","slug":"agenturl-must-use-http-or-https-got-scheme","errorCode":null,"errorMessage":"agentUrl must use http or https, got: {scheme}","messagePattern":"agentUrl must use http or https, got: (.+?)","errorType":"validation","errorClass":"NonRetryableException","httpStatus":null,"severity":"error","filePath":"ai/src/main/java/org/conductoross/conductor/ai/a2a/A2AService.java","lineNumber":440,"sourceCode":"    }\n\n    /**\n     * Guards against SSRF: rejects URLs whose hostname resolves to an RFC-1918 address, loopback,\n     * link-local (169.254.x.x — AWS/GCP/Azure metadata), or any non-http(s) scheme.\n     *\n     * <p>Note: DNS resolution is performed once here. A sufficiently hostile DNS server could\n     * rebind the name to a private IP after this check (TOCTOU). For stronger protection, deploy\n     * behind a network-layer firewall that blocks egress to private ranges.\n     */\n    public void validateAgentUrl(String rawUrl) {\n        if (rawUrl == null || rawUrl.isBlank()) {\n            throw new NonRetryableException(\"agentUrl must not be blank\");\n        }\n        try {\n            URL url = new URL(rawUrl.trim());\n            String scheme = url.getProtocol();\n            if (!\"http\".equals(scheme) && !\"https\".equals(scheme)) {\n                throw new NonRetryableException(\"agentUrl must use http or https, got: \" + scheme);\n            }\n            String host = url.getHost();\n            InetAddress[] addresses = InetAddress.getAllByName(host);\n            for (InetAddress addr : addresses) {\n                // Cloud metadata endpoints are blocked even when private networks are allowed.\n                if (isMetadataAddress(addr)) {\n                    A2AMetrics.ssrfBlocked();\n                    throw new NonRetryableException(\n                            \"agentUrl resolves to a cloud metadata address — SSRF blocked: \"\n                                    + addr.getHostAddress());\n                }\n                if (allowPrivateNetwork) {\n                    continue;\n                }\n                if (addr.isLoopbackAddress()\n                        || addr.isSiteLocalAddress()\n                        || addr.isLinkLocalAddress()\n                        || addr.isAnyLocalAddress()","sourceCodeStart":422,"sourceCodeEnd":458,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/ai/src/main/java/org/conductoross/conductor/ai/a2a/A2AService.java#L422-L458","documentation":"Thrown by A2AService.validateAgentUrl() when the URL uses a scheme other than http or https (e.g. file://, ftp://, gopher://). This is part of the SSRF protection layer and is a NonRetryableException (no retry, FAILED_WITH_TERMINAL_ERROR).","triggerScenarios":"The agentUrl string parses as a valid URL but the protocol/scheme is not 'http' or 'https'. For example: 'file:///etc/passwd', 'ftp://server/agent', 'gopher://internal-service'.","commonSituations":"The agentUrl was mistyped or contains a protocol prefix error. A malicious or misconfigured input attempts to use a non-HTTP protocol. The URL was constructed by string concatenation that accidentally produced a wrong scheme.","solutions":["Ensure the agentUrl uses http:// or https:// scheme","Correct any protocol typos in the agentUrl value","If testing locally, use http://localhost:port (note: localhost may also be blocked by SSRF rules unless allow-private-network is enabled)"],"exampleFix":"// before\n{\"agentUrl\": \"file:///path/to/agent\"}\n// after\n{\"agentUrl\": \"https://my-agent.example.com\"}","handlingStrategy":"validation","validationCode":"// Validate URL scheme before the A2A call\ntry {\n    URL url = new URL(agentUrl.trim());\n    if (!\"http\".equals(url.getProtocol()) && !\"https\".equals(url.getProtocol())) {\n        throw new IllegalArgumentException(\"agentUrl must use http or https\");\n    }\n} catch (MalformedURLException e) {\n    throw new IllegalArgumentException(\"Invalid agentUrl: \" + agentUrl, e);\n}","typeGuard":"public boolean isHttpOrHttpsUrl(String url) {\n    if (url == null || url.isBlank()) return false;\n    try {\n        String scheme = new URL(url.trim()).getProtocol();\n        return \"http\".equals(scheme) || \"https\".equals(scheme);\n    } catch (MalformedURLException e) {\n        return false;\n    }\n}","tryCatchPattern":"try {\n    a2aService.validateAgentUrl(agentUrl);\n} catch (NonRetryableException e) {\n    if (e.getMessage().contains(\"must use http or https\")) {\n        log.error(\"agentUrl has invalid scheme: {}\", e.getMessage());\n    }\n    throw e;\n}","preventionTips":["Always use http:// or https:// prefix in agentUrl","Validate URLs on the client/workflow side before passing to A2A tasks","Sanitize any user-provided URLs to reject non-HTTP schemes"],"tags":["a2a","ssrf","security","non-retryable"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}