Skip to content

Commit 4bcc122

Browse files
committed
fix: update RestErrorMapper to handle RFC 7807 Problem Details error format
This covers the core fix: the client's RestErrorMapper now reads the type URI from Problem Details responses (introduced by the server-side changes on this branch) instead of the old error/message fields, with backward compatibility for the legacy format. Signed-off-by: Emmanuel Hugonnet <ehugonne@redhat.com>
1 parent 56c1960 commit 4bcc122

File tree

4 files changed

+616
-112
lines changed

4 files changed

+616
-112
lines changed

client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,63 @@ public static A2AClientException mapRestError(String body, int code) {
3636
try {
3737
if (body != null && !body.isBlank()) {
3838
JsonObject node = JsonUtil.fromJson(body, JsonObject.class);
39+
// Support RFC 7807 Problem Details format (type, title, details, status)
40+
if (node.has("type")) {
41+
String type = node.get("type").getAsString();
42+
String errorMessage = node.has("title") ? node.get("title").getAsString() : "";
43+
return mapRestErrorByType(type, errorMessage, code);
44+
}
45+
// Legacy format (error, message)
3946
String className = node.has("error") ? node.get("error").getAsString() : "";
4047
String errorMessage = node.has("message") ? node.get("message").getAsString() : "";
41-
return mapRestError(className, errorMessage, code);
48+
return mapRestErrorByClassName(className, errorMessage, code);
4249
}
43-
return mapRestError("", "", code);
50+
return mapRestErrorByClassName("", "", code);
4451
} catch (JsonProcessingException ex) {
4552
Logger.getLogger(RestErrorMapper.class.getName()).log(Level.SEVERE, null, ex);
4653
return new A2AClientException("Failed to parse error response: " + ex.getMessage());
4754
}
4855
}
4956

5057
public static A2AClientException mapRestError(String className, String errorMessage, int code) {
58+
return mapRestErrorByClassName(className, errorMessage, code);
59+
}
60+
61+
/**
62+
* Maps RFC 7807 Problem Details error type URIs to A2A exceptions.
63+
* <p>
64+
* Note: Error constructors receive null for code and data parameters because:
65+
* <ul>
66+
* <li>Error codes are defaulted by each error class (e.g., -32007 for ExtendedAgentCardNotConfiguredError)</li>
67+
* <li>The message comes from the RFC 7807 "title" field</li>
68+
* <li>The data field is optional and not included in basic RFC 7807 responses</li>
69+
* </ul>
70+
*
71+
* @param type the RFC 7807 error type URI (e.g., "https://a2a-protocol.org/errors/task-not-found")
72+
* @param errorMessage the error message from the "title" field
73+
* @param code the HTTP status code (currently unused, kept for consistency)
74+
* @return an A2AClientException wrapping the appropriate A2A error
75+
*/
76+
private static A2AClientException mapRestErrorByType(String type, String errorMessage, int code) {
77+
return switch (type) {
78+
case "https://a2a-protocol.org/errors/task-not-found" -> new A2AClientException(errorMessage, new TaskNotFoundError());
79+
case "https://a2a-protocol.org/errors/extended-agent-card-not-configured" -> new A2AClientException(errorMessage, new ExtendedAgentCardNotConfiguredError(null, errorMessage, null));
80+
case "https://a2a-protocol.org/errors/content-type-not-supported" -> new A2AClientException(errorMessage, new ContentTypeNotSupportedError(null, errorMessage, null));
81+
case "https://a2a-protocol.org/errors/internal-error" -> new A2AClientException(errorMessage, new InternalError(errorMessage));
82+
case "https://a2a-protocol.org/errors/invalid-agent-response" -> new A2AClientException(errorMessage, new InvalidAgentResponseError(null, errorMessage, null));
83+
case "https://a2a-protocol.org/errors/invalid-params" -> new A2AClientException(errorMessage, new InvalidParamsError());
84+
case "https://a2a-protocol.org/errors/invalid-request" -> new A2AClientException(errorMessage, new InvalidRequestError());
85+
case "https://a2a-protocol.org/errors/method-not-found" -> new A2AClientException(errorMessage, new MethodNotFoundError());
86+
case "https://a2a-protocol.org/errors/push-notification-not-supported" -> new A2AClientException(errorMessage, new PushNotificationNotSupportedError());
87+
case "https://a2a-protocol.org/errors/task-not-cancelable" -> new A2AClientException(errorMessage, new TaskNotCancelableError());
88+
case "https://a2a-protocol.org/errors/unsupported-operation" -> new A2AClientException(errorMessage, new UnsupportedOperationError());
89+
case "https://a2a-protocol.org/errors/extension-support-required" -> new A2AClientException(errorMessage, new ExtensionSupportRequiredError(null, errorMessage, null));
90+
case "https://a2a-protocol.org/errors/version-not-supported" -> new A2AClientException(errorMessage, new VersionNotSupportedError(null, errorMessage, null));
91+
default -> new A2AClientException(errorMessage);
92+
};
93+
}
94+
95+
private static A2AClientException mapRestErrorByClassName(String className, String errorMessage, int code) {
5196
return switch (className) {
5297
case "io.a2a.spec.TaskNotFoundError" -> new A2AClientException(errorMessage, new TaskNotFoundError());
5398
case "io.a2a.spec.ExtendedCardNotConfiguredError" -> new A2AClientException(errorMessage, new ExtendedAgentCardNotConfiguredError(null, errorMessage, null));

0 commit comments

Comments
 (0)