Open
Description
What version of Remix are you using?
1.6.7
Steps to Reproduce
I'm executing a http request from a route Action but I'm getting this error because Server is busy (not responding):
[frontend] /frontend/node_modules/web-streams-polyfill/src/lib/readable-stream.ts:149
[frontend] return promiseRejectedWith(new TypeError('Cannot cancel a stream that already has a reader'));
[frontend] ^
[frontend] TypeError: Cannot cancel a stream that already has a reader
[frontend] at ReadableStream.cancel (/frontend/node_modules/web-streams-polyfill/src/lib/readable-stream.ts:149:34)
[frontend] at abort (/frontend/node_modules/@remix-run/web-fetch/src/fetch.js:75:18)
[frontend] at AbortSignal.abortAndFinalize (/frontend/node_modules/@remix-run/web-fetch/src/fetch.js:91:4)
[frontend] at AbortSignal.[nodejs.internal.kHybridDispatch] (node:internal/event_target:643:20)
[frontend] at AbortSignal.dispatchEvent (node:internal/event_target:585:26)
[frontend] at abortSignal (node:internal/abort_controller:284:10)
[frontend] at AbortController.abort (node:internal/abort_controller:315:5)
[frontend] at Timeout._onTimeout (/frontend/apps/webapp/app/utils/http.ts:16:42)
[frontend] at listOnTimeout (node:internal/timers:559:17)
[frontend] at processTimers (node:internal/timers:502:7)
I created a simple utility to support timeout with fetch:
const DEFAULT_TIMEOUT = 5000;
export type HttpOptions = RequestInit & {
timeout?: number;
defaultResponse?: unknown;
parseJSON?: boolean;
}
async function httpRequest(
resource: string,
options: HttpOptions = { timeout: DEFAULT_TIMEOUT, parseJSON: true },
) {
const { timeout, ...rest } = options;
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout || DEFAULT_TIMEOUT);
try {
const response = await fetch(resource, {
...rest,
signal: controller.signal
});
clearTimeout(id);
return rest.parseJSON === false ? response : response.json();
} catch (error) {
if (rest.defaultResponse !== undefined) {
return rest.defaultResponse;
}
if (controller.signal.aborted) {
throw new Error('Server is busy, please try again');
}
throw error;
}
}
export { httpRequest };
Then I'm using that method to execute a POST request:
export const action: ActionFunction = async ({ request }) => {
const formData = await request.formData();
const email = formData.get('email');
try {
await httpRequest(`${apiURL}/auth`, {
method: 'POST',
body: JSON.stringify({
email,
}),
parseJSON: false,
});
// ...
} catch (error) {
return {
error: (error as Error)?.message,
}
}
};
Also I'm already using CatchBoundary and ErrorBoundary for this page:
function renderLoginPage(error?: Error) {
const data = useLoaderData();
return (
<div id="main-content" className="relative pb-36">
<LoginPage {...data} error={error} />
</div>
);
}
export const CatchBoundary = () => {
const caught = useCatch();
return renderLoginPage(caught.data);
};
export const ErrorBoundary: ErrorBoundaryComponent = ({ error }) => {
return renderLoginPage(error);
};
export default function () {
return renderLoginPage();
}
Expected Behavior
Be able to get the error message using useActionData
Hook from Remix, it's working for other validations before executing the HTTP request.
Actual Behavior
Getting an unexpected exception:
Thanks for your help! <3