conductor-oss/conductor · error · NonRetryableException

agentUrl must not be blank

Error message

agentUrl must not be blank

What it means

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.

Source

Thrown at ai/src/main/java/org/conductoross/conductor/ai/a2a/A2AService.java:434

    private JsonNode parseBody(Response response, String body) throws Exception {
        String contentType = response.header("Content-Type", "application/json");
        if (contentType != null && contentType.contains("text/event-stream")) {
            return parseSseResponse(body);
        }
        return objectMapper.readTree(body);
    }

    /**
     * Guards against SSRF: rejects URLs whose hostname resolves to an RFC-1918 address, loopback,
     * link-local (169.254.x.x — AWS/GCP/Azure metadata), or any non-http(s) scheme.
     *
     * <p>Note: DNS resolution is performed once here. A sufficiently hostile DNS server could
     * rebind the name to a private IP after this check (TOCTOU). For stronger protection, deploy
     * behind a network-layer firewall that blocks egress to private ranges.
     */
    public void validateAgentUrl(String rawUrl) {
        if (rawUrl == null || rawUrl.isBlank()) {
            throw new NonRetryableException("agentUrl must not be blank");
        }
        try {
            URL url = new URL(rawUrl.trim());
            String scheme = url.getProtocol();
            if (!"http".equals(scheme) && !"https".equals(scheme)) {
                throw new NonRetryableException("agentUrl must use http or https, got: " + scheme);
            }
            String host = url.getHost();
            InetAddress[] addresses = InetAddress.getAllByName(host);
            for (InetAddress addr : addresses) {
                // Cloud metadata endpoints are blocked even when private networks are allowed.
                if (isMetadataAddress(addr)) {
                    A2AMetrics.ssrfBlocked();
                    throw new NonRetryableException(
                            "agentUrl resolves to a cloud metadata address — SSRF blocked: "
                                    + addr.getHostAddress());
                }
                if (allowPrivateNetwork) {

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. Ensure the agentUrl is a non-blank string in the task input parameters
  2. Verify any workflow variable references for agentUrl resolve to actual values
  3. Add input validation in the workflow to fail early with a clearer message if agentUrl is missing

Example fix

// before
{"agentUrl": "${agentUrl_var}"}  // variable is null
// after
{"agentUrl": "https://my-agent.example.com"}
Defensive patterns

Strategy: validation

Validate before calling

// Validate agentUrl before any A2A operation
if (agentUrl == null || agentUrl.isBlank()) {
    throw new IllegalArgumentException("agentUrl must not be blank");
}
// Or let A2AService.validateAgentUrl() handle it
a2aService.validateAgentUrl(agentUrl);

Type guard

public boolean isValidAgentUrl(String url) {
    return url != null && !url.isBlank();
}

Try / catch

try {
    a2aService.validateAgentUrl(agentUrl);
} catch (NonRetryableException e) {
    // NonRetryableException → task will be FAILED_WITH_TERMINAL_ERROR
    log.error("Invalid agentUrl: {}", e.getMessage());
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14). Data as JSON: /api/errors/4134755d895e4515. Report an issue: GitHub.