{"record":{"id":"4134755d895e4515","repo":"conductor-oss/conductor","slug":"agenturl-must-not-be-blank","errorCode":null,"errorMessage":"agentUrl must not be blank","messagePattern":"agentUrl must not be blank","errorType":"validation","errorClass":"NonRetryableException","httpStatus":null,"severity":"error","filePath":"ai/src/main/java/org/conductoross/conductor/ai/a2a/A2AService.java","lineNumber":434,"sourceCode":"    private JsonNode parseBody(Response response, String body) throws Exception {\n        String contentType = response.header(\"Content-Type\", \"application/json\");\n        if (contentType != null && contentType.contains(\"text/event-stream\")) {\n            return parseSseResponse(body);\n        }\n        return objectMapper.readTree(body);\n    }\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) {","sourceCodeStart":416,"sourceCodeEnd":452,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/ai/src/main/java/org/conductoross/conductor/ai/a2a/A2AService.java#L416-L452","documentation":"Thrown by A2AService.validateAgentUrl() when the agentUrl is null or blank (whitespace-only). This is a NonRetryableException, meaning the Conductor task will be marked as FAILED_WITH_TERMINAL_ERROR and will not be retried. This guard runs before any network call.","triggerScenarios":"Any A2A operation (send, stream, get task, cancel, agent-card discovery) is invoked with a null, empty, or whitespace-only agentUrl. validateAgentUrl() is the first check in jsonRpc() and the streaming method.","commonSituations":"The agentUrl input parameter was not set in the workflow task definition. The agentUrl was templated from a workflow variable that resolved to null. The A2ACallRequest or A2AAgentCardRequest was deserialized without an agentUrl field.","solutions":["Ensure the agentUrl is a non-blank string in the task input parameters","Verify any workflow variable references for agentUrl resolve to actual values","Add input validation in the workflow to fail early with a clearer message if agentUrl is missing"],"exampleFix":"// before\n{\"agentUrl\": \"${agentUrl_var}\"}  // variable is null\n// after\n{\"agentUrl\": \"https://my-agent.example.com\"}","handlingStrategy":"validation","validationCode":"// Validate agentUrl before any A2A operation\nif (agentUrl == null || agentUrl.isBlank()) {\n    throw new IllegalArgumentException(\"agentUrl must not be blank\");\n}\n// Or let A2AService.validateAgentUrl() handle it\na2aService.validateAgentUrl(agentUrl);","typeGuard":"public boolean isValidAgentUrl(String url) {\n    return url != null && !url.isBlank();\n}","tryCatchPattern":"try {\n    a2aService.validateAgentUrl(agentUrl);\n} catch (NonRetryableException e) {\n    // NonRetryableException → task will be FAILED_WITH_TERMINAL_ERROR\n    log.error(\"Invalid agentUrl: {}\", e.getMessage());\n    throw e;\n}","preventionTips":["Always set agentUrl in A2ACallRequest input parameters","Validate workflow input parameters before the A2A task runs","Use input schemas on the task definition to enforce non-blank agentUrl"],"tags":["a2a","ssrf","input-validation","non-retryable"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}