Skip to content

feat: add ExceptionHandler#142

Merged
wojiaodoubao merged 2 commits into
lance-format:mainfrom
wojiaodoubao:exception-handler
Jul 23, 2025
Merged

feat: add ExceptionHandler#142
wojiaodoubao merged 2 commits into
lance-format:mainfrom
wojiaodoubao:exception-handler

Conversation

@wojiaodoubao

@wojiaodoubao wojiaodoubao commented Jul 21, 2025

Copy link
Copy Markdown
Collaborator

Since we are implementing namespace, we need to throw errors, e.g. resource not found(#135 (comment),).

I think we need to first handle errors. Then we can throw errors in namespace impl.

@github-actions github-actions Bot added enhancement New feature or request java Java features labels Jul 21, 2025
private final String responseBody;
private final ErrorResponse errorResponse;

public LanceNamespaceException(ApiException e) {

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.

for this constructor, I think the expectation is to get the errorResponse from the ApiException, instead of hard-coding it to the values below

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.

Thanks! I would like to parse errorResponse from ApiException in the way below, please help review whether it is ok.

There are 2 cases of ApiException.

  1. Server-side error. ApiException is constructed from HttpResponse. Since the server-side error follows spec(https://github.com/lancedb/lance-namespace/blob/main/docs/src/spec/rest.yaml#L2513), we can parse error response from responseBody, which is a json object.
  2. Client-side error, such as io error, request serialize error. In this case, the response body is either null or can't be deserialized as json. We need to hard-code the errorResponse.

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.

For 1, I think we should still handle the case if server returns an error that is not following the spec and handle it probably as a 500 with logging.

For 2, I think it won't throw ApiException? Maybe we should define this as LanceNamespaceServerException, and have another LanceNamespaceClientException to handle the case you are describing, what do you think?

@wojiaodoubao wojiaodoubao Jul 22, 2025

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.

For 1, I think we should still handle the case if server returns an error that is not following the spec and handle it probably as a 500 with logging.

Agree. We can use 'whether containing both response header and response body' as the condition to identify if it is a server-side error or a client-side error.

For 2, I think it won't throw ApiException? Maybe we should define this as LanceNamespaceServerException, and have another LanceNamespaceClientException to handle the case you are describing, what do you think?

The client-side error will throw ApiException too, the code is auto-generated.
e.g.

Since the rest client always throwing ApiException and LanceNamespaceException is able to parse error response from ApiException, I think a LanceNamespaceException should be enough.

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.

I see.

If any error occurs, construct ErrorResponse with 500.

I think we cannot do that, because 500 indicates something is wrong with the server, but as you shown the error could be client side.

In that case, I would suggest let's make private final ErrorResponse errorResponse; as private final Optional<ErrorResponse> errorResponse; and only set that if it is available. Otherwise, just use the default Java Exception constructor to set super(message, throwable)

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 just found a way to identify 'server-side error' and 'client-side error'. Please help review whether the idea is ok.

We can use 'whether containing both response header and response body' as the condition to identify if it is a server-side error or a client-side error. Then we can use the correct code.

  private static ErrorResponse parse(ApiException e) {
    boolean serverSideError = e.getResponseHeaders() != null && e.getResponseBody() != null;
    ......
  }

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.

I guess to clarify our terms, there are actually 3 cases:

  1. something went wrong in the client before even sending the request to server
  2. server returns a 4XX error indicating client request has some issues
  3. server returns a 5XX error indicating server has some issues
  4. something went wrong in the client for processing the error response after receiving it from the server (e.g. error response structure is invalid and fails to be parsed)

I was talking about using Optional<ErrorResponse> errorResponse; and set it as empty for case 1 and 4, because it does not even make sense to say it belongs to any HTTP error code and we don't really have a valid error response.

This means we can do something like

public LanceNamespaceException(ApiException e) {
    // try parse response to ErrorResponse
    // if succeed, use this(errorResponse) constructor
    // if null or fail parsing, just do super(e)
}

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.

Thanks your explanation, it is clear, let me fix it.

errorResponse.setTitle("Malformed request");
errorResponse.setStatus(400);
errorResponse.setDetail(detail);
errorResponse.setInstance("/v1/namespaces");

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.

the instance should be the actual URI route that failed, not hard-coded

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.

Thanks! I misunderstood the meaning of instance, let me fix it.

I also made 'type' hard-coded, is it ok?

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.

I think the intention for type is to provide more detailed error type beyond the standard HTTP error codes. For example if there is any error code from HMS, or in the example above we could also make UnknownDBException an error type.

Also for title vs detail, my thinking was that an error message like String.format("Database %s doesn't exist.", database) should be the title, and things like stacktrace should be detail.

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.

@jackye1995 @wojiaodoubao for non rest implementations do we still want to use this instance type?

@wojiaodoubao wojiaodoubao Jul 26, 2025

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.

Hi @geruh , I think we still need this even for non rest impl. Mostly is because 'non rest impl' could be used by the rest server. Following this rule can make the response more clear.

If we are using the 'non rest impl' directly, we might not need the status part and detail part(containing the stack trace).

My idea is:

  1. We use zero as undefined status.
  2. We ignore undefined status and detail in Exception message. So when we directly use impl, we won't see the confusing message.

What do you think?

@wojiaodoubao wojiaodoubao marked this pull request as draft July 22, 2025 06:09
@wojiaodoubao wojiaodoubao force-pushed the exception-handler branch 3 times, most recently from 235afb7 to d4988a2 Compare July 22, 2025 08:05
@wojiaodoubao wojiaodoubao marked this pull request as ready for review July 22, 2025 08:06
@wojiaodoubao wojiaodoubao force-pushed the exception-handler branch 2 times, most recently from baf85f5 to 27beda0 Compare July 23, 2025 07:20

@jackye1995 jackye1995 left a comment

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.

looks good to me!

@jackye1995

Copy link
Copy Markdown
Collaborator

@wojiaodoubao I just invited you as maintainer, so we can iterate quickly on this and you can just merge by yourself after rebase.

@wojiaodoubao wojiaodoubao merged commit a81f637 into lance-format:main Jul 23, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request java Java features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants