Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throws more useful error when fetch fails #2921

Merged
merged 5 commits into from
Apr 4, 2024
Merged

Conversation

Pessimistress
Copy link
Collaborator

@Pessimistress Pessimistress commented Mar 17, 2024

Same response, before:

message: Failed to fetch resource https://some.url/not/even/very/long...
  at <trace_stack>

After:

message:  Failed to fetch resource (500): https://some.url/not/even/very/long...
  at <trace_stack>
cause: {
  url: 'https://some.url/not/even/very/long',
  response: '{message: "server died"}'
}

Copy link
Collaborator

@ibgreen ibgreen left a comment

Choose a reason for hiding this comment

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

If the goal is to have human readable errors suitable for presentation in a UI, then starting the string with an http error code is not great.
If we are going to have something presentable like "load failed" that string should come first.

  • Apps should no longer rely on string parsing but use the cause

  • If we add a cause mighn't we as well create a LoadError class so that callers can reliably detect that this is from loaders and we can type the cause.

  • Based on experience apps will not use the cause, it would be there for frameworks like deck.gl so make sure it meets your needs.

  • to set expectations, since this affects apps, I would want this to be a 4.2 feature with some what's new documentation, not slip it in as a patch release. If this is a blocker for deck.gl maybe we can do this in two parts - add the 'cause' for deck.gl as a patch in 4.1 and sweat the message changes in 4.2

const message = await getResponseError(response);
throw new Error(message);
const error = await getResponseError(response);
throw error;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess creating the error inside makes the error stack trace point to a different place. just trying to consider the debug experience, but this is probably ok.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The stack trace will be getResponseError ... checkResponse ... <caller of checkResponse> ... so basically one extra layer on top of whatever it was before. If it matters to you then we can move the content of getResponseError into this function since this function doesn't do anything else.

text += ` ${await response.text()}`;
cause.response = await response.json();
} else {
cause.response = await response.text();
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

json can be very messy and if this is e.g. xml it can get crazy. This is part of why including this verbatim is so problematic.

modules/core/src/lib/utils/response-utils.ts Outdated Show resolved Hide resolved
@Pessimistress
Copy link
Collaborator Author

I am fine with putting the error code after the message, i.e. "Failed to fetch resources (500)"

since this affects apps

What app is this affecting exactly? Is any app relying on a trimmed URL in the message? I have a hard time believing it because the following message is 60 characters:

Failed to fetch resource https://github.com/visgl/loaders.gl

@Pessimistress Pessimistress requested a review from ibgreen March 26, 2024 20:43
Copy link
Collaborator

@ibgreen ibgreen left a comment

Choose a reason for hiding this comment

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

OK I pushed some changes, ready to merge.

  • I prefer to create a FetchError subclass than to start adding arbitrary data to Error.cause, details in comments.
  • Added a url shortener.

let message = `Failed to fetch resource (${response.status}): ${response.url}`;
message = message.length > 65 ? `${message.slice(0, 65)}...` : message;

const cause: {
Copy link
Collaborator

Choose a reason for hiding this comment

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

IMHO, cause is really intended to be used for linking to another exception. It is used when rethrowing exceptions to link to the original Error.

While the MDN docs do state that cause does not have to be an Error instance, to me this is similar to saying that a caught exception is not guaranteed to be an Error instance. Technically it can happen but it is unlikely and very annoying when it does.

One problem with supplying cause as an object is that there is no way to check what type it is, in contrast to exporting an Error subclass where the app can use instanceof to safely access any additional members.

if (contentType?.includes('application/json')) {
text += ` ${await response.text()}`;
cause.response = await response.json();
Copy link
Collaborator

Choose a reason for hiding this comment

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

If we do have a response field on the error shouldn't it be the actual Response object?

@ibgreen ibgreen merged commit 51af025 into master Apr 4, 2024
1 check passed
@ibgreen ibgreen deleted the x/response-message branch April 4, 2024 22:56
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.

2 participants