{"record":{"id":"cde970ac89c5a9a6","repo":"conductor-oss/conductor","slug":"agenturl-must-not-be-blank-cde970","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/tasks/worker/A2AWorkers.java","lineNumber":352,"sourceCode":"        } finally {\n            recordOutcome(result);\n        }\n    }\n\n    /**\n     * Resolves the JSON-RPC endpoint while keeping the configured discovery identity intact.\n     *\n     * <p>The workflow editor stores the fetched Agent Card in task metadata. Prefer its advertised\n     * endpoint so a task configured with a website or well-known card URL does not POST JSON-RPC to\n     * the discovery document. API-authored workflows without a snapshot still support direct Agent\n     * Card URLs by discovering the endpoint at execution time. Direct endpoint URLs remain valid\n     * and all returned values are trimmed before the SSRF-guarded A2A service uses them.\n     */\n    private String resolveRemoteEndpoint(\n            Task task, String configuredUrl, Map<String, String> headers) {\n        String configured = StringUtils.trimToNull(configuredUrl);\n        if (configured == null) {\n            throw new NonRetryableException(\"agentUrl must not be blank\");\n        }\n\n        String snapshottedEndpoint = endpointFromSnapshot(task);\n        if (snapshottedEndpoint != null) {\n            return snapshottedEndpoint;\n        }\n\n        String pathWithoutQuery = StringUtils.substringBefore(configured, \"?\");\n        if (StringUtils.endsWithIgnoreCase(pathWithoutQuery, \".json\")) {\n            AgentCard card = a2aService.getAgentCard(configured, headers);\n            String discoveredEndpoint = card == null ? null : StringUtils.trimToNull(card.getUrl());\n            if (discoveredEndpoint == null) {\n                throw new NonRetryableException(\n                        \"Agent Card resolved from \"\n                                + configured\n                                + \" does not advertise a JSON-RPC 'url'\");\n            }\n            return discoveredEndpoint;","sourceCodeStart":334,"sourceCodeEnd":370,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/ai/src/main/java/org/conductoross/conductor/ai/tasks/worker/A2AWorkers.java#L334-L370","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure 'agentUrl' is present and non-blank on AGENT and CANCEL_AGENT task inputs.","When propagating cancellation, forward the original agentUrl or rely on the snapshotted endpoint in task metadata.","Validate the agentUrl input at workflow authoring time.","If the URL is discovered from an Agent Card (.json), confirm the card advertises a non-blank 'url' field."],"exampleFix":"// before\nA2ACallRequest req = ...; // agentUrl is null/whitespace\nString endpoint = resolveRemoteEndpoint(task, req.getAgentUrl(), headers); // throws\n\n// after\nif (StringUtils.isBlank(req.getAgentUrl())) {\n    // surface a clear validation error before resolving\n    throw new IllegalArgumentException(\"agentUrl is required for remote A2A calls\");\n}\nString endpoint = resolveRemoteEndpoint(task, req.getAgentUrl(), headers);","handlingStrategy":"validation","validationCode":"if (StringUtils.isBlank(request.getAgentUrl())) {\n    throw new IllegalArgumentException(\n        \"AGENT/CANCEL_AGENT requires a non-blank 'agentUrl'\");\n}","typeGuard":null,"tryCatchPattern":"// NonRetryableException is terminal; validate before calling resolveRemoteEndpoint.\ntry {\n    workers.agent(request);\n} catch (NonRetryableException e) {\n    if (e.getMessage().contains(\"agentUrl must not be blank\")) {\n        // fix the task input; do not retry\n    }\n}","preventionTips":["Always include a non-blank 'agentUrl' on AGENT and CANCEL_AGENT task inputs.","Forward the original agentUrl when propagating cancellations.","Ensure snapshotted endpoint metadata is present for resolved agents.","Validate agentUrl is non-blank before the task is scheduled."],"tags":["a2a","endpoint-resolution","validation","non-retryable","agent-url"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}