Skip to content
DocsErrorsCannot access data, headers, params, searchParams, or a short-lived cache a Suspense boundary nor a `"use cache"` above it.

Cannot access data, headers, params, searchParams, or a short-lived cache a Suspense boundary nor a `"use cache"` above it.

Why This Error Occurred

When the experimental flag dynamicIO is enabled, Next.js expects a parent Suspense boundary around any component that awaits data that should be accessed on every user request. The purpose of this requirement is so that Next.js can provide a useful fallback while this data is accessed and rendered.

While some data is inherently only available when a user request is being handled, such as request headers, Next.js assumes that by default any asynchronous data is expected to be accessed each time a user request is being handled unless you specifically cache it using "use cache".

The proper fix for this specific error depends on what data you are accessing and how you want your Next.js app to behave.

Possible Ways to Fix It

Accessing Data

When you access data using fetch, a database client, or any other module which does asynchronous IO, Next.js interprets your intent as expecting the data to load on every user request.

If you are expecting this data to be used while fully or partially prerendering a page you must cache is using "use cache".

Before:

app/page.js
async function getRecentArticles() {
  return db.query(...)
}
 
export default async function Page() {
  const articles = await getRecentArticles(token);
  return <ArticleList articles={articles}>
}

After:

app/page.js
async function getRecentArticles() {
  "use cache"
  // This cache can be revalidated by webhook or server action
  // when you call revalidateTag("articles")
  cacheTag("articles")
  // This cache will revalidate after an hour even if no explicit
  // revalidate instruction was received
  cacheLife('hours')
  return db.query(...)
}
 
export default async function Page() {
  const articles = await getRecentArticles(token);
  return <ArticleList articles={articles}>
}

If this data should be accessed on every user request you must provide a fallback UI using Suspense from React. Where you put this Suspense boundary in your application should be informed by the kind of fallback UI you want to render. It can be immediately above the component accessing this data or even in your Root Layout.

Before:

app/page.js
async function getLatestTransactions() {
  return db.query(...)
}
 
export default async function Page() {
  const transactions = await getLatestTransactions(token);
  return <TransactionList transactions={transactions}>
}

After:

app/page.js
import { Suspense } from 'react'
 
async function getLatestTransactions() {
  return db.query(...)
}
 
function TransactionSkeleton() {
  return <div>...</div>
}
 
export default async function Page() {
  const transactions = await getLatestTransactions(token);
  return (
    <Suspense fallback={<TransactionSkeleton />}>
      <TransactionList transactions={transactions}>
    </Suspense>
  )
}
Headers

If you are accessing request headers using headers(), cookies(), or draftMode(). Consider whether you can move the use of these APIs deeper into your existing component tree.

Before:

app/inbox.js
export async function Inbox({ token }) {
  const email = await getEmail(token)
  return (
    <ul>
      {email.map((e) => (
        <EmailRow key={e.id} />
      ))}
    </ul>
  )
}
app/page.js
import { cookies } from 'next/headers'
 
import { Inbox } from './inbox'
 
export default async function Page() {
  const token = (await cookies()).get('token')
  return (
    <Suspense fallback="loading your inbox...">
      <Inbox token={token}>
    </Suspense>
  )
}

After:

app/inbox.js
import { cookies } from 'next/headers'
 
export async function Inbox() {
  const token = (await cookies()).get('token')
  const email = await getEmail(token)
  return (
    <ul>
      {email.map((e) => (
        <EmailRow key={e.id} />
      ))}
    </ul>
  )
}
app/page.js
import { Inbox } from './inbox'
 
export default async function Page() {
  return (
    <Suspense fallback="loading your inbox...">
      <Inbox>
    </Suspense>
  )
}

Alternatively you can add a Suspense boundary above the component that is accessing Request headers.

Params and SearchParams

Layout params, and Page params and searchParams props are promises. If you await them in the Layout or Page component you might be accessing these props higher than is actually required. Try passing these props to deeper components as a promise and awaiting them closer to where the actual param or searchParam is required

Before:

app/map.js
export async function Map({ lat, lng }) {
  const mapData = await fetch(`https://...?lat=${lat}&lng=${lng}`)
  return drawMap(mapData)
}
app/page.js
import { cookies } from 'next/headers'
 
import { Map } from './map'
 
export default async function Page({ searchParams }) {
  const { lat, lng } = await searchParams;
  return (
    <Suspense fallback="loading your inbox...">
      <Map lat={lat} lng={lng}>
    </Suspense>
  )
}

After:

app/map.js
export async function Map({ coords }) {
  const { lat, lng } = await coords
  const mapData = await fetch(`https://...?lat=${lat}&lng=${lng}`)
  return drawMap(mapData)
}
app/page.js
import { cookies } from 'next/headers'
 
import { Map } from './map'
 
export default async function Page({ searchParams }) {
  const coords = searchParams.then(sp => { lat: sp.lat, lng: sp.lng })
  return (
    <Suspense fallback="loading your inbox...">
      <Map coord={coords}>
    </Suspense>
  )
}

Alternatively you can add a Suspense boundary above the component that is accessing params or searchParams so Next.js understands what UI should be used when while waiting for this request data to be accessed.

Short-lived Caches

"use cache" allows you to describe a cacheLife() that might be too short to be practical to prerender. The utility of doing this is that it can still describe a non-zero caching time for the client router cache to reuse the cache entry in the browser and it can also be useful for protecting upstream APIs while experiencing high request traffic.

If you expected the "use cache" entry to be prerenderable try describing a slightly longer cacheLife().

Before:

app/page.js
async function getDashboard() {
  "use cache"
  // This cache will revalidate after 1 second. It is so short
  // Next.js won't prerender it on the server but the client router
  // can reuse the result for up to 30 seconds unless the user manually refreshes
  cacheLife('seconds')
  return db.query(...)
}
 
export default async function Page() {
  const data = await getDashboard(token);
  return <Dashboard data={data}>
}

After:

app/page.js
async function getDashboard() {
  "use cache"
  // This cache will revalidate after 1 minute. It's long enough that
  // Next.js will still produce a fully or partially prerendered page
  cacheLife('minutes')
  return db.query(...)
}
 
export default async function Page() {
  const data = await getDashboard(token);
  return <Dashboard data={data}>
}

Alternatively you can add a Suspense boundary above the component that is accessing this short-lived cached data so Next.js understands what UI should be used while accessing this data on a user request.