Skip to content

Add error classes - #22

Merged
fjuma merged 5 commits into
a2aproject:mainfrom
kabir:errors-1
May 8, 2025
Merged

fjuma merged 5 commits into
a2aproject:mainfrom
kabir:errors-1

Conversation

@kabir

@kabir kabir commented May 7, 2025

Copy link
Copy Markdown
Collaborator

Fixes #1

@@ -0,0 +1,4 @@
package io.a2a.spec;

public class A2AClientError extends Exception {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not 100% sure here, the Python implementation is:

class A2AClientError(Exception):
    pass

@fjuma fjuma May 7, 2025 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could add a constructor here that just takes a string message?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pass means "no implementation" in python. It's like an interface in Java, just for typing

@maeste maeste May 8, 2025 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well bad explanation...since it extends Exception class, in python it inherits also its constructor. IOW pass just pass anything to the super class. So this code should work

def a_method():
    raise A2AClientError("Connection failed")

try:
    amehod()
except A2AClientError as e:
    print(f"Exception message: {e}")

and it should print something like: Exception message: Connection failed

@@ -0,0 +1,4 @@
package io.a2a.spec;

public class A2AClientJSONError extends A2AClientError {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not totally sure here either, here is the Python

class A2AClientJSONError(A2AClientError):
    def __init__(self, message: str):
        self.message = message
        super().__init__(f'JSON Error: {message}')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like they're just prefixing the message string with "JSON Error". We can always do that later if we think it's useful. I see now that A2AClientJSONError isn't in a2a.json so what we do here is mostly up to us.

@@ -0,0 +1,5 @@
package io.a2a.spec;

public class MissingAPIKeyError extends Exception {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same doubt as for the A2AClientError

* Represents a JSONRPC error.
*/
public record JSONRPCError(int code, String message, Object data) {
public class JSONRPCError {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to check, any reason to convert this to a class? This is immutable and we're using Jackson to convert to/from JSON. It looks like the MCP Java SDK used records for errors like this.

@fjuma fjuma May 7, 2025 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see now, we need to use a class because the other errors need to extend this one, right?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fjuma Yeah, I wasn't able to inherit from Records. It says they are final


public class UnsupportedOperationError extends JSONRPCError {

public UnsupportedOperationError(int code, String message, Object data) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For classes like this that extend JSONRPCError, I believe we'll need the @JsonCreator annotation on the constructor and @JsonProperty("blah") annotations on each parameter to be able to successfully create the object from JSON. Let me know if I'm missing something though.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to read up a bit on this :-)

public class UnsupportedOperationError extends JSONRPCError {

public UnsupportedOperationError(int code, String message, Object data) {
super(code, message, data);

@fjuma fjuma May 7, 2025 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the spec specifies default values for code and message:

https://github.com/google/A2A/blob/main/specification/json/a2a.json#L1450
https://github.com/google/A2A/blob/main/samples/python/common/types.py#L303-L304

Looks like the other error types also have defaults that we should use as well.

Assert.checkNotNullParam("message", message);
this.code = code;
this.message = message;
this.data = data;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is data needed here? I don't see it in the python implementation for this one.

@kabir

kabir commented May 8, 2025

Copy link
Copy Markdown
Collaborator Author

Hi @fjuma PR updated.

For the root JSONRPCError class I wasn't sure if this should have the annotations, or if it is always one of the subclasses. I originally made it abstract, but looking at spec json, a lot of the Response classes simply have the error as JSONRPCError. So, I think the errors must be getting deserialized on the client, and thus ended up putting the annotations on this class anyway.

I see in the test it is simply deserialized as JSONRPCError. So I'm now wondering a bit if the sub-classes actually need the annotations, but don't know yet whether they are needed or not. Perhaps they are used for the serialization.

@JsonProperty("message") String message,
@JsonProperty("data") Object data) {
super(
defaultIfNull(code, -32702),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be -32602

@JsonProperty("message") String message,
@JsonProperty("data") Object data) {
super(
defaultIfNull(code, -32704),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be -32004

@fjuma

fjuma commented May 8, 2025

Copy link
Copy Markdown
Contributor

Looks good, just a couple small comments on the error codes.

I see in the test it is simply deserialized as JSONRPCError. So I'm now wondering a bit if the sub-classes actually need the annotations, but don't know yet whether they are needed or not. Perhaps they are used for the serialization.

For Part and its sub-classes, I was able to use @JsonTypeInfo and @JsonSubTypes to make sure the correct class gets instantiated:

https://github.com/fjuma/a2a-java-sdk/blob/main/src/main/java/io/a2a/spec/Part.java#L12-L21

For JSONRPCError and its sub-classes, there isn't a type field so this approach wouldn't work there to get the correct class to be instantiated. Maybe we'd need a custom deserializer for something like that? We could always do that as a follow-up issue.

@fjuma

fjuma commented May 8, 2025

Copy link
Copy Markdown
Contributor

@kabir I created #23 to track looking into deserialization for classes that extend JSONRPCError to see if the correct class can be instantiated based on the error code.

I'll go ahead and merge this PR and submit a small fix for the two error codes. Thanks!

@fjuma
fjuma merged commit 370b2d3 into a2aproject:main May 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement error types

3 participants