[React 19] useTranslations Hook Causes "Expected a Suspended Thenable" Error in Async React Components Requiring Client-Side Rendering
bramteunis opened this issue · 4 comments
Bug Report
React version: 19
Description of the Bug
When creating an async React component and calling the useTranslations
hook, which requires client-side rendering, React does not provide an error message indicating the issue. Instead, it throws an internal error with the following message:
Internal error: Error: Expected a suspended thenable. This is a bug in React. Please file an issue.
at getSuspendedThenable ...
at retryTask ...
at performWork ...
It appears that React is not correctly handling the combination of useTranslations
(a client-side rendering hook) inside an asynchronous component. This leads to React attempting to suspend improperly, resulting in the error.
Steps to Reproduce
- Create an async React component.
- Use the
useTranslations
hook inside the component. - Attempt to render the component in a server-side context or without proper client-side rendering setup.
- Observe the error described above.
Link to Code Example
Here is a minimal example demonstrating the issue:
import { useTranslations } from 'next-intl';
const AsyncComponent = async () => {
const t = useTranslations(); // Requires client-side rendering
return <div>{t('exampleKey')}</div>;
};
export default AsyncComponent;
The Current Behavior
Instead of providing a clear error indicating the need for client-side rendering or proper handling of hooks within async components, React throws the following error:
Internal error: Error: Expected a suspended thenable. This is a bug in React. Please file an issue.
at getSuspendedThenable ...
at retryTask ...
at performWork ...
Additionally, errors such as Invalid state: ReadableStream is already closed
and failed to pipe response
are observed, leading to significant debugging overhead.
The Expected Behavior
React should:
- Provide a clear error message that explains why
useTranslations
cannot be used in this context (e.g., "useTranslations must be called in a client-side rendering context"). - Avoid internal errors such as "Expected a suspended thenable."
This would help developers quickly identify and fix the issue without requiring extensive debugging of React internals.
Hi,
I’ve implemented a change that directly addresses the unclear error message encountered when hooks like useTranslations are improperly used in server-rendering or asynchronous contexts. The updated logic ensures that React throws a descriptive error when encountering an invalid "thenable" or improper usage of client-side-only hooks.
Code Change
Here’s the modification I made to the getSuspendedThenable function:
if (suspendedThenable === null || typeof suspendedThenable.then !== 'function') {
// Check if the thrown value is a valid thenable (i.e., a Promise-like object).
// React relies on thenables for managing Suspense during asynchronous rendering.
// If the thrown value is invalid (null, undefined, or not a thenable),
// an error is thrown with a clear message explaining the issue.
//
// This ensures that async components or hooks, such as `useTranslations`, are
// only used in client-side rendering contexts, and not during server-side
// rendering, where they can lead to incorrect behavior and errors.
throw new Error(
'Invalid use of async components or hooks like `useTranslations` in server ' +
'rendering context. Ensure all hooks are used in synchronous client-rendered ' +
'components.'
);
}
Outcome
With this change, if an invalid thenable is encountered or a client-side-only hook is used incorrectly during server rendering, developers now receive a clear error message:
"Invalid use of async components or hooks like useTranslations in server rendering context. Ensure all hooks are used in synchronous client-rendered components."
This eliminates the ambiguous "Expected a suspended thenable" error, which was difficult to debug.
Request for Feedback
I’d appreciate feedback on whether this change sufficiently addresses the issue. Should I proceed with further enhancements, such as implementing a general validation mechanism for all client-side-only hooks? Or is this level of error handling adequate for this case?
Looking forward to your thoughts!
Perfect, i do think this error handling suits this use case well enough.