Error Boundary does not catch errors of the resource

Hello, I have a resource created and access it inside a suspense inside an error boundary, but if the resource throws an error, e.g. no network, then the error boundary does not catch this and the suspense fallback gets displayed. Why?
<ErrorBoundary fallback={ErrorFallback}>
<Suspense
fallback={
<For each={Array(4).fill(0)}>
{(_, index) => (
<div class={index() == 0 ? "" : "mt-4"}>
<PetSkeleton></PetSkeleton>
</div>
)}
</For>
}
>
<For
each={
families()?.find(
(family) => family.id == selectedFamily()
)?.pets
}
>
<ErrorBoundary fallback={ErrorFallback}>
<Suspense
fallback={
<For each={Array(4).fill(0)}>
{(_, index) => (
<div class={index() == 0 ? "" : "mt-4"}>
<PetSkeleton></PetSkeleton>
</div>
)}
</For>
}
>
<For
each={
families()?.find(
(family) => family.id == selectedFamily()
)?.pets
}
>
const [families, { mutate: mutateFamilies }] = createResource(fetchFamilies);
const [families, { mutate: mutateFamilies }] = createResource(fetchFamilies);
export const fetchFamilies = async () => {
const response: Family[] = await sendApiRequest(
"/api/families",
"GET",
false
);
return response.sort((a, b) => a.name.localeCompare(b.name));
};
export const fetchFamilies = async () => {
const response: Family[] = await sendApiRequest(
"/api/families",
"GET",
false
);
return response.sort((a, b) => a.name.localeCompare(b.name));
};
28 Replies
Greenman999
Greenman999OP4w ago
export const sendApiRequest = async (
url: string,
method: string,
shouldDisplaySuccessAlert: boolean = true,
body?: any
) => {
try {
const response = await fetch(url, {
method,
headers: body && { "Content-Type": "application/json" },
body: body && JSON.stringify(body),
});
let json;
try {
json = await response.json();
} catch (e) {
throw new Error("Failed to parse server response: " + e.message);
}

if (!response.ok) throw new Error(json.message);
if (shouldDisplaySuccessAlert)
AlertManager.addAlert(json.message, "success");
return json.data;
} catch (error) {
AlertManager.addAlert(error.message, "error", 3000);
throw error;
}
};
export const sendApiRequest = async (
url: string,
method: string,
shouldDisplaySuccessAlert: boolean = true,
body?: any
) => {
try {
const response = await fetch(url, {
method,
headers: body && { "Content-Type": "application/json" },
body: body && JSON.stringify(body),
});
let json;
try {
json = await response.json();
} catch (e) {
throw new Error("Failed to parse server response: " + e.message);
}

if (!response.ok) throw new Error(json.message);
if (shouldDisplaySuccessAlert)
AlertManager.addAlert(json.message, "success");
return json.data;
} catch (error) {
AlertManager.addAlert(error.message, "error", 3000);
throw error;
}
};
peerreynders
peerreynders4w ago
The ErrorBoundary will only catch errors thrown from components that are inside it. I suspect that your createResource is running above the JSX that creates that boundary, so it will never see the errors that are thrown.
Greenman999
Greenman999OP3w ago
how can i move the createResource inside it? until now the resource is just at the top typescript code of the component
ladybluenotes
ladybluenotes3w ago
it may be easier to check the state using one of the state values vs wrapping it in an ErrorBoundary - https://docs.solidjs.com/reference/basic-reactivity/create-resource
Greenman999
Greenman999OP3w ago
hm even if the fetcher throws the network error, state is still pending and error is undefined
ladybluenotes
ladybluenotes3w ago
What kind of behaviour are you looking for? Like if you’re getting a network error, what do you hope to see?
Greenman999
Greenman999OP3w ago
that the component displays an error message such as “You have no Internet connection” instead of the fetched content
ladybluenotes
ladybluenotes3w ago
you could maybe use the unresolved state value in that instance
Greenman999
Greenman999OP3w ago
but the state is pending somehow, not unresolved
ladybluenotes
ladybluenotes3w ago
possible you can make a simplified recreation stackblitz?
Greenman999
Greenman999OP3w ago
uhh i can try
Greenman999
Greenman999OP3w ago
if you block the api url in the inspector networks tab to simulate no internet, the status is pending
ladybluenotes
ladybluenotes3w ago
So I looked into it and I suspect there may be an error with the backend (I can't find anything wrong on the client side at all). The hanging "pending" state kind of signals to me it's probably the backend itself, have you checked out the api itself?
Greenman999
Greenman999OP3w ago
no it can't be the backend because im blocking the request in the browser, because of this it cant even reach the backend, right?
peerreynders
peerreynders3w ago
By moving the error boundary to the containing component (i.e. create a container component if there isn't one). https://playground.solidjs.com/anonymous/0dbbc510-4e61-4b9a-b94c-5841b7fbfc69
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Greenman999
Greenman999OP3w ago
but even with this if i load the solidjs playground, then disconnect my wifi and then click the little reload button, the state is pending. only after another click on the reload button the error appears?
peerreynders
peerreynders3w ago
I forced the fetch to fail by changing the https to http. The error boundary works with a fast an definitive error like that. Turning off wifi will have it waiting until it finally times out, so an initial pending state would be expected. Typically connection status is assessed via the Network information API https://developer.mozilla.org/en-US/docs/Web/API/Network_Information_API
MDN Web Docs
Network Information API - Web APIs | MDN
The Network Information API provides information about the system's connection in terms of general connection type (e.g., 'wifi, 'cellular', etc.). This can be used to select high definition content or low definition content based on the user's connection.
Greenman999
Greenman999OP3w ago
okay thanks, will try to implement it!
peerreynders
peerreynders3w ago
MDN Web Docs
Navigator: onLine property - Web APIs | MDN
Returns the online status of the browser. The property returns a boolean value, with true meaning online and false meaning offline. The property sends updates whenever the browser's ability to connect to the network changes. The update occurs when the user follows links or when a script requests a remote page. For example, the property should re...
peerreynders
peerreynders3w ago
Go to the demo mentioned here: https://discord.com/channels/722131463138705510/861229287868858379/1316078418571558984 and remove both error boundaries. Clicking either button won't affect the displayed resource status but the error will be reported in the console. It seems that errors from the fetcher manage to escape from the resource without finalizing resource state. Because everybody is using error boundaries nobody seems to have noticed that .error doesn't seem to work as intended.
ladybluenotes
ladybluenotes3w ago
so this is a createResource issue?
peerreynders
peerreynders3w ago
This example works (from this discussion) https://playground.solidjs.com/anonymous/64aaf0b4-b237-4a6f-b70c-698f1a1ca1f3 I'm not exactly clear where the line is (i.e. perhaps my expectations are wrong)
GitHub
Errors during createResource fetches · solidjs solid · Discussion #...
Describe the bug Hi there, I'm really struggling with errors coming from createResource. No matter what I do, errors seem to go uncaught. The resource state is updated to 'errored' but ...
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
peerreynders
peerreynders3w ago
OK, so it seems that .state, ,loading, .error appear in the view in the state they were when the resource was read. To get the expected behaviour use
!users.loading && !users.error && users()
!users.loading && !users.error && users()
instead of just
users()
users()
That way the resource isn't read until the ready state is reached. https://playground.solidjs.com/anonymous/29454ad3-a758-473c-ac9f-75c4834a3ca8 Background: https://github.com/solidjs/solid/discussions/1888
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
GitHub
Errors during createResource fetches · solidjs solid · Discussion #...
Describe the bug Hi there, I'm really struggling with errors coming from createResource. No matter what I do, errors seem to go uncaught. The resource state is updated to 'errored' but ...
Brendonovich
Brendonovich3w ago
so it seems that .state, ,loading, .error appear in the view in the state they were when the resource was read.
i don't think this is the case, they appear according to whatever the current value is, but when you do users() on its own i think any error will flow through to the root-level owner and essentially break all reactivity, stopping them from updating 😅 unless what you were saying only applies to the properties after reactivity has already been frozen, which does make sense
peerreynders
peerreynders3w ago
If you use this component instead:
function Comp() {
const [users, { refetch }] = createResource(fetcher);

let poll = setInterval(() => {
console.log(users.state, users.loading, Date.now(), users.error?.message);
}, 1000);

onCleanup(() => clearInterval(poll));

return (
<div>
<p>Loading: {users.loading.toString()}</p>
<p>Error: {users.error?.message}</p>
<p>State: {users.state}</p>
<button onClick={refetch}>Refetch users</button>
<Show when={users()}>
{(data) => (
<ul>
<For each={data()}>{(user) => <li>{user.name}</li>}</For>
</ul>
)}
</Show>
</div>
);
}
function Comp() {
const [users, { refetch }] = createResource(fetcher);

let poll = setInterval(() => {
console.log(users.state, users.loading, Date.now(), users.error?.message);
}, 1000);

onCleanup(() => clearInterval(poll));

return (
<div>
<p>Loading: {users.loading.toString()}</p>
<p>Error: {users.error?.message}</p>
<p>State: {users.state}</p>
<button onClick={refetch}>Refetch users</button>
<Show when={users()}>
{(data) => (
<ul>
<For each={data()}>{(user) => <li>{user.name}</li>}</For>
</ul>
)}
</Show>
</div>
);
}
You can see that the view gets stuck in the 'Loading: true' state while the poll in the console shows:
errored false 1735612463872 Failed to fetch
errored false 1735612463872 Failed to fetch
I'm just reporting my observations.
Brendonovich
Brendonovich3w ago
yeah that checks out, the uncaught error breaks the ui's reactivity but the resource's internal state is still correct oh lol even after it errors you can hit refetch and it can go back to the ready state, ui just doesn't show it
peerreynders
peerreynders3w ago
Conclusion: use the d*mn error boundary and move it high enough to catch everything.

Did you find this page helpful?