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
- 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
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
- 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
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
- agentUrl must use http or https, got: {scheme}
- agentUrl resolves to a cloud metadata address — SSRF blocked
- agentUrl resolves to a private/reserved address — SSRF block
- agentUrl is not a valid URL: {rawUrl} — {message}
- GET_AGENT_CARD requires 'agentUrl'
AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14).
Data as JSON: /api/errors/4134755d895e4515.
Report an issue: GitHub.