{"record":{"id":"9b85a9b6539f89a4","repo":"facebook/react","slug":"an-unknown-component-is-an-async-client-component","errorCode":null,"errorMessage":"An unknown Component is an async Client Component. Only Server Components can be async at the moment. This error is often caused by accidentally adding `'use client'` to a module that was originally written for the server.","messagePattern":"An unknown Component is an async Client Component\\. Only Server Components can be async at the moment\\. This error is often caused by accidentally adding `'use client'` to a module that was originally written for the server\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-reconciler/src/ReactFiberThenable.js","lineNumber":267,"sourceCode":"        // This is an uncached thenable that we haven't seen before.\n\n        // Detect infinite ping loops caused by uncached promises.\n        const root = getWorkInProgressRoot();\n        if (root !== null && root.shellSuspendCounter > 100) {\n          // This root has suspended repeatedly in the shell without making any\n          // progress (i.e. committing something). This is highly suggestive of\n          // an infinite ping loop, often caused by an accidental Async Client\n          // Component.\n          //\n          // During a transition, we can suspend the work loop until the promise\n          // to resolve, but this is a sync render, so that's not an option. We\n          // also can't show a fallback, because none was provided. So our last\n          // resort is to throw an error.\n          //\n          // TODO: Remove this error in a future release. Other ways of handling\n          // this case include forcing a concurrent render, or putting the whole\n          // root into offscreen mode.\n          throw new Error(\n            'An unknown Component is an async Client Component. ' +\n              'Only Server Components can be async at the moment. ' +\n              'This error is often caused by accidentally ' +\n              \"adding `'use client'` to a module that was originally written \" +\n              'for the server.',\n          );\n        }\n\n        const pendingThenable: PendingThenable<T> = thenable as any;\n        pendingThenable.status = 'pending';\n        pendingThenable.then(\n          fulfilledValue => {\n            if (thenable.status === 'pending') {\n              const fulfilledThenable: FulfilledThenable<T> = thenable as any;\n              fulfilledThenable.status = 'fulfilled';\n              fulfilledThenable.value = fulfilledValue;\n            }\n          },","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/facebook/react/blob/eafeac097ba51e1eab809c07102126bd5f8e5425/packages/react-reconciler/src/ReactFiberThenable.js#L249-L285","documentation":"When a component suspends on a thenable React has not seen before (no status field), React marks it pending, attaches listeners, pings the root to retry, and throws to suspend. If the root suspends in the shell more than 100 times during a sync render without committing anything, React concludes the ping loop is infinite. The canonical cause is an async Client Component — an async function component running on the client returns a fresh promise every render that never resolves into rendered output.","triggerScenarios":"A synchronous render where a component repeatedly suspends on brand-new (uncached) thenables: an async function component in client code ('use client' module), or inline creation of promises passed to use() on every render (e.g. use(fetch('/x')) in the render body).","commonSituations":"Accidentally adding 'use client' to a module that still exports async server components (Next.js App Router); client components declared async out of habit; forgetting to cache/hoist promises consumed with use(); RSC boundaries drawn in the wrong place.","solutions":["Remove async from the client component: fetch data in a Server Component and pass it (or a cached promise) down as props","If suspending from a client component, pass use() a stable promise — create it once per input and cache it (module-level cache or React.cache on the server)","Move data fetching into actions/transitions so results update state instead of inline awaits during render","Audit every 'use client' file for async function components"],"exampleFix":"// before — 'use client' module\n'use client';\nexport default async function Notes() { // async Client Component -> ping loop\n  const notes = await db.notes();\n  return <List notes={notes} />;\n}\n\n// after — keep it a Server Component (no 'use client'):\n// notes.tsx (server)\nexport default async function Notes() {\n  const notes = await db.notes();\n  return <List notes={notes} />; // List stays a client component\n}","handlingStrategy":"validation","validationCode":"// Cache promises per input so use() never sees a fresh thenable each render\nconst promiseCache = new Map<string, Promise<Data>>();\nfunction getData(id: string): Promise<Data> {\n  let p = promiseCache.get(id);\n  if (!p) {\n    p = fetch(`/api/${id}`).then((r) => r.json());\n    promiseCache.set(id, p);\n  }\n  return p;\n}","typeGuard":"// Reject async function components in client modules\nfunction isAsyncComponent(fn: unknown): boolean {\n  return (\n    typeof fn === 'function' &&\n    (fn.constructor?.name === 'AsyncFunction' ||\n      Object.prototype.toString.call(fn) === '[object AsyncFunction]')\n  );\n}","tryCatchPattern":null,"preventionTips":["Never declare a client component as async; keep async data fetching in Server Components","Only pass cached, stable promises to use() — create them once per input and reuse","Add a lint/test check that every exported component in a 'use client' file is a sync function","Keep server/client boundaries explicit: data fetching on the server, interaction on the client"],"tags":["react","suspense","use","async-component","rsc","server-components","nextjs"],"backgroundTag":"async-client-component","analyzedSha":"eafeac097ba51e1eab809c07102126bd5f8e5425","analyzedAt":"2026-08-21T22:01:08.818Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}