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

Bug: When I Use use() in server component, get [Error: Expected a suspended thenable. This is a bug in React. Please file an issue.] #32483

Open
buddle6091 opened this issue Feb 27, 2025 · 2 comments
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug

Comments

@buddle6091
Copy link

React version: 19.0.0

Steps To Reproduce

  1. I tried to getting data at server by using use() and avoid using useEffect() in client component for performance

Link to code example:

'use server'

import { use } from 'react'
import { getItsmeStores } from '@/actions/itsme-v1/store/action'
import ItsmeStoreRadio from '@/components/store-radio/itsme-store-radio'
import React, { Suspense } from 'react'
import Loading from '@/app/(main)/store/product-categories/loading'
import { getItsmeProductCategories } from '@/actions/itsme-v1/store/category/action'
import StoreProductCategoriesContent from '@/app/(main)/store/product-categories/_components/content'

type StoreProductCategoriesPageProps = {
  searchParams: { [key: string]: string | undefined }
}

const StoreProductCategoriesPage = async (
  props: StoreProductCategoriesPageProps,
) => {
  const { searchParams } = props
  const storeID = searchParams?.store_id ?? ''
  const stores = use(getItsmeStores()) // ❗️ getting error
  const initialCategories = use(getItsmeProductCategories(storeID)) //❗️ getting error

  return (
    <Suspense fallback={<Loading />}>
      <ItsmeStoreRadio stores={stores} />
      <StoreProductCategoriesContent
        storeID={storeID as string}
        initialCategories={initialCategories}
      />
    </Suspense>
  )
}

export default StoreProductCategoriesPage

The current behavior

  • And then, I could find the error in terminal
  ⨯ [Error: failed to pipe response] {
[cause]: [Error: Expected a suspended thenable. This is a bug in React. Please file an issue.] {
 digest: '1982665415'
  }
 }

so I tried to getting static storeID instead of getting searchParams by props.
But, it didn`t work.. :(

The expected behavior

But, I found the reason...!
Remove async() is the answer.

And if you don't mind, I would appreciate it if you could explain in detail the cause of this issue.

thank you.

vercel/next.js#51477

@buddle6091 buddle6091 added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Feb 27, 2025
@Mayank6787
Copy link

Fixing "Expected a Suspended Thenable" Error in React Server Components

Cause of the Issue

The error "Expected a suspended thenable" occurs because the use() hook can only be used inside React Server Components (RSCs) and cannot be used within an async component. Your component is marked as async, which conflicts with use().

Why Does This Happen?

  1. use() is for Server Components

    • use() is designed to be used in React Server Components to fetch data without useEffect().
    • It expects a Thenable (Promise that suspends the component).
  2. async Component Conflicts with use()

    • In React, when a function is marked as async, it implicitly returns a Promise.
    • use() expects to handle promises itself, but an async component tries to handle them too, leading to the conflict and unexpected behavior.

Solution: Remove async Keyword

Since use() automatically suspends and resolves the promise, marking the component as async is unnecessary and causes an issue. Simply remove async from StoreProductCategoriesPage, like this:

'use server'

import { use } from 'react'
import { getItsmeStores } from '@/actions/itsme-v1/store/action'
import ItsmeStoreRadio from '@/components/store-radio/itsme-store-radio'
import React, { Suspense } from 'react'
import Loading from '@/app/(main)/store/product-categories/loading'
import { getItsmeProductCategories } from '@/actions/itsme-v1/store/category/action'
import StoreProductCategoriesContent from '@/app/(main)/store/product-categories/_components/content'

type StoreProductCategoriesPageProps = {
  searchParams: { [key: string]: string | undefined }
}

const StoreProductCategoriesPage = (props: StoreProductCategoriesPageProps) => {
  const { searchParams } = props
  const storeID = searchParams?.store_id ?? ''
  const stores = use(getItsmeStores()) // ✅ No error
  const initialCategories = use(getItsmeProductCategories(storeID)) // ✅ No error

  return (
    <Suspense fallback={<Loading />}>
      <ItsmeStoreRadio stores={stores} />
      <StoreProductCategoriesContent
        storeID={storeID as string}
        initialCategories={initialCategories}
      />
    </Suspense>
  )
}

export default StoreProductCategoriesPage

Key Takeaways

  • Use use() inside Server Components only.
  • Do NOT mark the component as async. Let use() handle the promise suspension.
  • React expects a Thenable from use(), and using async interferes with React’s internal suspension mechanism.
  • If you need to use async/await, fetch data outside the component and pass it as props instead of using use().

This fix ensures your component works correctly without React's "Expected a suspended thenable" error.

@denk0403
Copy link

denk0403 commented Mar 1, 2025

Your code has several issues which are misusing React, and likely putting it in an unexpected state which causes the error.

  1. The 'use server'; directive is for marking functions in a module as "server functions". These functions effectively become API routes that the client can use to run code on the server. The directive should not be used at the top of files containing components.

  2. In async components, the use() function cannot be called. I don't think this is explicitly documented, but if you have eslint-plugin-react-hooks installed, it will warn against this. Instead, you should await promises in async components.

    • Note: use() can still be used in server components, they just can't be async. And in client components, use() should generally only be given promises created and streamed from the server.
  3. Components with use() or await should be nested inside Suspense boundaries; the components themselves typically do not contain Suspense boundaries (unless you're doing some kind of incremental reveal of content). If you're using Next.js, the easiest thing to do is to add a loading.tsx file at the same route segment.

Putting these fixes together, I think you should update your code to the following:

// in page.tsx

import { getItsmeStores } from '@/actions/itsme-v1/store/action'
import ItsmeStoreRadio from '@/components/store-radio/itsme-store-radio'
import React, { Suspense } from 'react'
import { getItsmeProductCategories } from '@/actions/itsme-v1/store/category/action'
import StoreProductCategoriesContent from '@/app/(main)/store/product-categories/_components/content'

type StoreProductCategoriesPageProps = {
  searchParams: { [key: string]: string | undefined }
}

const StoreProductCategoriesPage = async (
  props: StoreProductCategoriesPageProps,
) => {
  const { searchParams } = props
  const storeID = searchParams?.store_id ?? ''
  const stores = await getItsmeStores()
  const initialCategories = await getItsmeProductCategories(storeID)

  return (
    <>
      <ItsmeStoreRadio stores={stores} />
      <StoreProductCategoriesContent
        storeID={storeID as string}
        initialCategories={initialCategories}
      />
    </>
  )
}

export default StoreProductCategoriesPage
// in loading.tsx

import Loading from '@/app/(main)/store/product-categories/loading'

export default LoadingFallback() {
 return <Loading />
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug
Projects
None yet
Development

No branches or pull requests

3 participants