If I use the onError mutateoptions why cant I rethrow error and make notification provider catch it?

If I use the onError in mutateoptions why cant I rethrow error and make notification provider catch it?
6 Replies
kapa.ai
kapa.ai2w ago
Hi @Todilo. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- The issue you're encountering relates to how error handling works in refine's data hooks and the notification system. Based on the knowledge sources, there are a few key points that explain why rethrowing errors in onError mutation options doesn't trigger the notification provider as you might expect. Why rethrowing doesn't work When you use onError in mutation options (like with useUpdate, useDelete, etc.), you're intercepting the error at a point where the notification system has already processed it. The error handling flow in refine works like this: 1. The data hook (e.g., useUpdate) catches the error from the API call 2. It automatically processes this error, including sending it to the notification provider 3. Then it calls your custom onError handler By the time your custom onError function runs, the notification has already been triggered. Rethrowing the error at this point won't cause the notification provider to catch it again because the error handling flow has already moved past the notification stage. As shown in the refine architecture code:
onError: (err: TError) => {
checkError(err);
queryOptions?.onError?.(err);

const notificationConfig =
typeof errorNotification === "function"
? errorNotification(err, { metaData, config }, resource)
: errorNotification;

handleNotification(notificationConfig, {
key: `${resource}-useList-notification`,
message: translate(
"common:notifications.error",
{ statusCode: err.statusCode },
`Error (status code: ${err.statusCode})`,
),
description: err.message,
type: "error",
});
},
onError: (err: TError) => {
checkError(err);
queryOptions?.onError?.(err);

const notificationConfig =
typeof errorNotification === "function"
? errorNotification(err, { metaData, config }, resource)
: errorNotification;

handleNotification(notificationConfig, {
key: `${resource}-useList-notification`,
message: translate(
"common:notifications.error",
{ statusCode: err.statusCode },
`Error (status code: ${err.statusCode})`,
),
description: err.message,
type: "error",
});
},
Better approaches for custom error handling Instead of trying to rethrow errors, you have several better options: 1. Use the errorNotification property Many refine hooks like useUpdate, useDelete, and useForm accept an errorNotification property that lets you customize the error notification:
const { mutate } = useUpdate();

mutate({
errorNotification: (data, values, resource) => {
return {
message: `Something went wrong when getting ${data.id}`,
description: "Error",
type: "error",
};
},
});
const { mutate } = useUpdate();

mutate({
errorNotification: (data, values, resource) => {
return {
message: `Something went wrong when getting ${data.id}`,
description: "Error",
type: "error",
};
},
});
This approach is recommended in the useUpdate documentation. 2. Use try-catch with useNotification For custom event handlers where you need more control, you can use the useNotification hook directly with try-catch blocks, as suggested in a GitHub issue response:
try {
// Your code that might throw an error
} catch (error) {
// Use useNotification to show custom error
notification.open({
type: "error",
message: "Custom error message",
description: error.message
});
}
try {
// Your code that might throw an error
} catch (error) {
// Use useNotification to show custom error
notification.open({
type: "error",
message: "Custom error message",
description: error.message
});
}
3. For form errors specifically If you're working with forms and want to display server errors in the form itself (not just notifications), you might need to manually handle this using the onMutationError callback, as discussed in this GitHub issue. Error boundary limitations It's also worth noting that React's error boundaries (which might be part of your error handling strategy) have limitations and don't catch errors in event handlers or asynchronous code, as mentioned in the Error Boundaries in React blog post. For the most robust error handling, consider using a combination of refine's built-in error notification system and custom error handling for specific cases. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@207945811592478720> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
conventional-tan
conventional-tanOP2w ago
@kapa.ai not sure what I am doing wrong but if I use the onError callback in mutationoptions I no longer get the notification, the useupdate onerror seem to no longer be called
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- You're encountering a known behavior in refine's data hooks. When you provide your own onError callback in mutationOptions, it overrides refine's default error handling behavior, including the automatic error notifications. This is documented in several places in the refine codebase. For example, in the useDelete documentation:
`mutationOptions` does not support `onSuccess` and `onError` props because they override the default `onSuccess` and `onError` functions.
`mutationOptions` does not support `onSuccess` and `onError` props because they override the default `onSuccess` and `onError` functions.
useDelete documentation The same applies to useUpdate and other data hooks. When you provide your own error handler, refine assumes you want to take complete control of error handling. Solutions: 1. Use the errorNotification property instead Instead of using onError in mutationOptions, use the errorNotification property:
const { mutate } = useUpdate();

mutate({
resource: "products",
id: 1,
values: { /* your values */ },
errorNotification: (data, values, resource) => {
// Your custom error handling logic here
return {
message: `Something went wrong when updating ${resource}`,
description: "Error",
type: "error",
};
},
});
const { mutate } = useUpdate();

mutate({
resource: "products",
id: 1,
values: { /* your values */ },
errorNotification: (data, values, resource) => {
// Your custom error handling logic here
return {
message: `Something went wrong when updating ${resource}`,
description: "Error",
type: "error",
};
},
});
useUpdate errorNotification documentation 2. Call the notification manually in your onError handler If you need to use onError for other reasons, you can manually trigger the notification:
import { useNotification } from "@refinedev/core";

// In your component
const { open } = useNotification();
const { mutate } = useUpdate({
mutationOptions: {
onError: (error, variables, context) => {
// Your custom error handling logic

// Manually trigger notification
open({
message: `Error updating resource`,
description: error.message,
type: "error"
});
}
}
});
import { useNotification } from "@refinedev/core";

// In your component
const { open } = useNotification();
const { mutate } = useUpdate({
mutationOptions: {
onError: (error, variables, context) => {
// Your custom error handling logic

// Manually trigger notification
open({
message: `Error updating resource`,
description: error.message,
type: "error"
});
}
}
});
This issue is a known behavior in refine, as mentioned in GitHub issue #5888, where it was noted that providing custom callbacks can override refine's default behaviors. The recommended approach is to use the dedicated errorNotification property when you want to customize notifications while preserving refine's error handling flow. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@207945811592478720> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
conventional-tan
conventional-tanOP2w ago
For anyone who might wander here the github issue https://github.com/refinedev/refine/issues/5888 solved my problem. When calling the mutate I change to mutateAsync . I wanted to redirect and/or re-enable my form buttons after mutate and I used mutateAsync(...).finally(() => { cleanup here, or redirect })
GitHub
[BUG] mutationOptions are overriding Refine's behavior unexpected...
Describe the bug Consider this: import { useLogin } from "@refinedev/core"; const { mutateAsync: login, isLoading } = useLogin({ mutationOptions: { onSuccess(data) { console.log("Hey...

Did you find this page helpful?