conductor-oss/conductor · error · NonRetryableException

agentUrl must not be blank

Error message

agentUrl must not be blank

What it means

Thrown by A2AWorkers.resolveRemoteEndpoint as NonRetryableException when the configured URL trims to null. resolveRemoteEndpoint is called from the AGENT, CANCEL_AGENT, and cancel() paths after agentType validation passes, to map a discovery/card URL to the JSON-RPC endpoint. A blank agentUrl at this stage is terminal (non-retryable).

Source

Thrown at ai/src/main/java/org/conductoross/conductor/ai/tasks/worker/A2AWorkers.java:352

        } finally {
            recordOutcome(result);
        }
    }

    /**
     * Resolves the JSON-RPC endpoint while keeping the configured discovery identity intact.
     *
     * <p>The workflow editor stores the fetched Agent Card in task metadata. Prefer its advertised
     * endpoint so a task configured with a website or well-known card URL does not POST JSON-RPC to
     * the discovery document. API-authored workflows without a snapshot still support direct Agent
     * Card URLs by discovering the endpoint at execution time. Direct endpoint URLs remain valid
     * and all returned values are trimmed before the SSRF-guarded A2A service uses them.
     */
    private String resolveRemoteEndpoint(
            Task task, String configuredUrl, Map<String, String> headers) {
        String configured = StringUtils.trimToNull(configuredUrl);
        if (configured == null) {
            throw new NonRetryableException("agentUrl must not be blank");
        }

        String snapshottedEndpoint = endpointFromSnapshot(task);
        if (snapshottedEndpoint != null) {
            return snapshottedEndpoint;
        }

        String pathWithoutQuery = StringUtils.substringBefore(configured, "?");
        if (StringUtils.endsWithIgnoreCase(pathWithoutQuery, ".json")) {
            AgentCard card = a2aService.getAgentCard(configured, headers);
            String discoveredEndpoint = card == null ? null : StringUtils.trimToNull(card.getUrl());
            if (discoveredEndpoint == null) {
                throw new NonRetryableException(
                        "Agent Card resolved from "
                                + configured
                                + " does not advertise a JSON-RPC 'url'");
            }
            return discoveredEndpoint;

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. Ensure 'agentUrl' is present and non-blank on AGENT and CANCEL_AGENT task inputs.
  2. When propagating cancellation, forward the original agentUrl or rely on the snapshotted endpoint in task metadata.
  3. Validate the agentUrl input at workflow authoring time.
  4. If the URL is discovered from an Agent Card (.json), confirm the card advertises a non-blank 'url' field.

Example fix

// before
A2ACallRequest req = ...; // agentUrl is null/whitespace
String endpoint = resolveRemoteEndpoint(task, req.getAgentUrl(), headers); // throws

// after
if (StringUtils.isBlank(req.getAgentUrl())) {
    // surface a clear validation error before resolving
    throw new IllegalArgumentException("agentUrl is required for remote A2A calls");
}
String endpoint = resolveRemoteEndpoint(task, req.getAgentUrl(), headers);
Defensive patterns

Strategy: validation

Validate before calling

if (StringUtils.isBlank(request.getAgentUrl())) {
    throw new IllegalArgumentException(
        "AGENT/CANCEL_AGENT requires a non-blank 'agentUrl'");
}

Try / catch

// NonRetryableException is terminal; validate before calling resolveRemoteEndpoint.
try {
    workers.agent(request);
} catch (NonRetryableException e) {
    if (e.getMessage().contains("agentUrl must not be blank")) {
        // fix the task input; do not retry
    }
}

Prevention

When it happens

Trigger: An AGENT or CANCEL_AGENT task where agentUrl is empty/whitespace and no snapshotted endpoint exists in task metadata; reached when resolving the remote endpoint to send/cancel the A2A message. Note executeRemote checks agentUrl blank earlier (line 311) and fail()s, but resolveRemoteEndpoint is also invoked from cancelAgent (line 250) and the TaskCancellationHandler.cancel() path (line 281) where this guard fires.

Common situations: A CANCEL_AGENT or cancellation propagation path where the original agentUrl was not stored/forwarded, a dynamically-resolved agentUrl that came back empty, or task metadata lacking a snapshotted a2a agentCard url.

Related errors


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