amplify-hosting icon indicating copy to clipboard operation
amplify-hosting copied to clipboard

Next.JS Fails to Update Prerender Cache with Time-based Revalidation

Open owenfarrell opened this issue 2 years ago • 15 comments

Before opening, please confirm:

  • [X] I have checked to see if my question is addressed in the FAQ.
  • [X] I have searched for duplicate or closed issues.
  • [X] I have read the guide for submitting bug reports.
  • [X] I have done my best to include a minimal, self-contained set of instructions for consistently reproducing the issue.
  • [X] I have removed any sensitive information from my code snippets and submission.

App Id

d3v90eh9vhzxl4

AWS Region

us-east-1

Amplify Hosting feature

SSR

Frontend framework

Next.js

Next.js version

13.4.12

Next.js router

App Router

Describe the bug

Next.JS is unable to update the prerender cache as part of the incremental site regeneration process. Since the web compute runtime is unable to write out regenerated pages to the filesystem, Amplify continues to serve up stale pages.

Expected behavior

When specifying a revalidation interval, Amplify web compute regenerates pages based on (re)fetched data and serves the regenerated pages up to visitors based on cache controls.

Reproduction steps

  1. Create a Next.JS application using the app router
  2. Enable time-based revalidation (documented here)
  3. Deploy to Amplify

Build Settings

version: 1
frontend:
  phases:
    preBuild:
      commands:
        - npm ci
    build:
      commands:
        - env | grep -e MY_CREDENTIALS_PREFIX_ >> .env.production
        - npm run build
  artifacts:
    baseDirectory: .next
    files:
      - '**/*'
  cache:
    paths:
      - node_modules/**/*
      - .next/cache/**/*

Log output

Failed to update prerender cache for /my-page [Error: EROFS: read-only file system, open '/tmp/app/.next/server/app/my-page.html'] {	
  errno: -30,	
  code: 'EROFS',	
  syscall: 'open',	
  path: '/tmp/app/.next/server/app/my-page.html'	
}

Additional information

No response

owenfarrell avatar Sep 15 '23 15:09 owenfarrell

Possibly related to https://github.com/aws-amplify/amplify-hosting/issues/3271

owenfarrell avatar Sep 21 '23 13:09 owenfarrell

Hi @owenfarrell @grundmanise 👋 , thanks for raising this and apologies for the inconvenience caused due to this behavior with the pre-rendering of the cache during the ISR process.

I was able to reproduce this behavior and am currently investigating into it further. I'll keep you posted with any updates.

Jay2113 avatar Sep 25 '23 17:09 Jay2113

I had the same issue here. Using NextJS 13.5.4

lucasrivoiro avatar Oct 05 '23 02:10 lucasrivoiro

Any updates on this issue?

gabstv avatar Oct 24 '23 10:10 gabstv

any updates? i also have this problem

pwzkrz avatar Jan 18 '24 14:01 pwzkrz

I found issues dating back to 2022 about planned support for ISR. It is now 2024. Is this still a planned feature?

https://github.com/aws-amplify/amplify-hosting/issues/3116

mcfry avatar Feb 29 '24 17:02 mcfry

I have the same log output .

Failed to update prerender cache for /index [Error: EROFS: read-only file system, open '/tmp/app/.next/server/app/index.html']

And it seems this issue occurs only for App Router pages.

I downloaded the build artifact and checked compute/run.sh. It seemas that Amplify Hosting creates the directory .next/server/pages as special.

#!/bin/bash
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_SESSION_TOKEN

mkdir -p '/tmp/app/.next/server/pages'
echo '[x-amplify-log][INFO] starting copy'
cp -r '/var/task/.next/server/pages' '/tmp/app/.next/server'
echo '[x-amplify-log][INFO] copy server pages complete'

cp -f /var/task/server.js /tmp/app/server.js
echo '[x-amplify-log][INFO] symlink creation complete path=.next'
ln -sf '/var/task/.next/server/app' '/tmp/app/.next/server/app'
# other sym links

node /tmp/app/server.js
Full run.sh
#!/bin/bash
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_SESSION_TOKEN
mkdir -p '/tmp/app/.next/server/pages'
echo '[x-amplify-log][INFO] starting copy'
cp -r '/var/task/.next/server/pages' '/tmp/app/.next/server'
echo '[x-amplify-log][INFO] copy server pages complete'
cp -f /var/task/server.js /tmp/app/server.js
echo '[x-amplify-log][INFO] copy complete'
ln -sf '/var/task/amplify-compute-bundle-output' '/tmp/app/amplify-compute-bundle-output'
ln -sf '/var/task/node_modules' '/tmp/app/node_modules'
ln -sf '/var/task/package.json' '/tmp/app/package.json'
ln -sf '/var/task/public' '/tmp/app/public'
echo '[x-amplify-log][INFO] Root symlink creation complete'
ln -sf '/var/task/.next/BUILD_ID' '/tmp/app/.next/BUILD_ID'
ln -sf '/var/task/.next/app-build-manifest.json' '/tmp/app/.next/app-build-manifest.json'
ln -sf '/var/task/.next/app-path-routes-manifest.json' '/tmp/app/.next/app-path-routes-manifest.json'
ln -sf '/var/task/.next/build-manifest.json' '/tmp/app/.next/build-manifest.json'
ln -sf '/var/task/.next/export-marker.json' '/tmp/app/.next/export-marker.json'
ln -sf '/var/task/.next/images-manifest.json' '/tmp/app/.next/images-manifest.json'
ln -sf '/var/task/.next/next-minimal-server.js.nft.json' '/tmp/app/.next/next-minimal-server.js.nft.json'
ln -sf '/var/task/.next/next-server.js.nft.json' '/tmp/app/.next/next-server.js.nft.json'
ln -sf '/var/task/.next/package.json' '/tmp/app/.next/package.json'
ln -sf '/var/task/.next/prerender-manifest.js' '/tmp/app/.next/prerender-manifest.js'
ln -sf '/var/task/.next/prerender-manifest.json' '/tmp/app/.next/prerender-manifest.json'
ln -sf '/var/task/.next/react-loadable-manifest.json' '/tmp/app/.next/react-loadable-manifest.json'
ln -sf '/var/task/.next/required-server-files.json' '/tmp/app/.next/required-server-files.json'
ln -sf '/var/task/.next/routes-manifest.json' '/tmp/app/.next/routes-manifest.json'
ln -sf '/var/task/.next/static' '/tmp/app/.next/static'
ln -sf '/var/task/.next/trace' '/tmp/app/.next/trace'
ln -sf '/var/task/.next/types' '/tmp/app/.next/types'
echo '[x-amplify-log][INFO] symlink creation complete path=.next'
ln -sf '/var/task/.next/server/app' '/tmp/app/.next/server/app'
ln -sf '/var/task/.next/server/app-paths-manifest.json' '/tmp/app/.next/server/app-paths-manifest.json'
ln -sf '/var/task/.next/server/chunks' '/tmp/app/.next/server/chunks'
ln -sf '/var/task/.next/server/font-manifest.json' '/tmp/app/.next/server/font-manifest.json'
ln -sf '/var/task/.next/server/functions-config-manifest.json' '/tmp/app/.next/server/functions-config-manifest.json'
ln -sf '/var/task/.next/server/interception-route-rewrite-manifest.js' '/tmp/app/.next/server/interception-route-rewrite-manifest.js'
ln -sf '/var/task/.next/server/middleware-build-manifest.js' '/tmp/app/.next/server/middleware-build-manifest.js'
ln -sf '/var/task/.next/server/middleware-manifest.json' '/tmp/app/.next/server/middleware-manifest.json'
ln -sf '/var/task/.next/server/middleware-react-loadable-manifest.js' '/tmp/app/.next/server/middleware-react-loadable-manifest.js'
ln -sf '/var/task/.next/server/next-font-manifest.js' '/tmp/app/.next/server/next-font-manifest.js'
ln -sf '/var/task/.next/server/next-font-manifest.json' '/tmp/app/.next/server/next-font-manifest.json'
ln -sf '/var/task/.next/server/pages-manifest.json' '/tmp/app/.next/server/pages-manifest.json'
ln -sf '/var/task/.next/server/server-reference-manifest.js' '/tmp/app/.next/server/server-reference-manifest.js'
ln -sf '/var/task/.next/server/server-reference-manifest.json' '/tmp/app/.next/server/server-reference-manifest.json'
ln -sf '/var/task/.next/server/webpack-runtime.js' '/tmp/app/.next/server/webpack-runtime.js'
echo '[x-amplify-log][INFO] symlink creation complete path=.next/server'
echo '[x-amplify-log][INFO] Starting Server'
node /tmp/app/server.js

Next.js App Router stores cache files under not .next/server/pages but .next/server/app. So I think this run.sh should not use symlink via ln -sf '/var/task/.next/server/app' '/tmp/app/.next/server/app' but create dir like cp -r '/var/task/.next/server/app' '/tmp/app/.next/server' .

Additional info

  • Next.js ver: 14.2.2
  • GitHub Repo: https://github.com/Quramy/next-js-amplify-study
  • My Amplify app id: d25rciaxjd9k8z
  • Build artifact: https://aws-amplify-prod-us-east-1-artifacts.s3.us-east-1.amazonaws.com/d25rciaxjd9k8z/main/0000000002/BUILD/artifacts.zip

Quramy avatar Apr 19 '24 14:04 Quramy

Hi @Jay2113 san, we are also facing the same issue while processing ISR based on App Router. Any update about the investigation?

Our Next.js version is 14.2.1.

If needed any further info, please ask to me.

hokahokabob avatar Jun 20 '24 04:06 hokahokabob

Hi Team, any update on this? So does this mean that we cannot use AWS amplify hosting for next js applications?

I get same error EROFS.

2024-07-03T02:45:18.887Z | START RequestId: f3d682f1-404a-4617-bd83-3ffeb19ac756 Version: $LATEST -- | --
  | 2024-07-03T02:45:18.930Z | REPORT RequestId: f3d682f1-404a-4617-bd83-3ffeb19ac756 Duration: 42.62 ms Billed Duration: 43 ms Memory Size: 1024 MB Max Memory Used: 140 MB
  | 2024-07-03T02:45:19.036Z | Failed to update prerender cache for /gpu [Error: EROFS: read-only file system, open '/tmp/app/.next/server/app/gpu.html'] {
  | 2024-07-03T02:45:19.036Z | errno: -30,
  | 2024-07-03T02:45:19.036Z | code: 'EROFS',
  | 2024-07-03T02:45:19.036Z | syscall: 'open',
  | 2024-07-03T02:45:19.036Z | path: '/tmp/app/.next/server/app/gpu.html'
  | 2024-07-03T02:45:19.036Z | }

This is my gpu/page.tsx code

export const revalidate = 120; // in seconds

async function getData(): Promise<GpuListing[]> {
    try {
        const data = getDataFromS3("db/gpu.csv");       
        return data;
    } catch (error) {
        console.error('Error fetching data:', error);
        return [];
    }
}

export default async function Gpu() {
    const data = await getData();
    const datetime_str = await getDateTime();
    return (
        <div>
            <header className="bg-white shadow">
                <div className="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8">
                    <h1 className="text-3xl font-bold tracking-tight text-gray-900">GPU (Graphics Card)</h1>
                </div>
            </header>
            <main>
                <div className="mx-auto max-w-7xl py-6 sm:px-6 lg:px-8">
                 <DataTable columns={columns} data={data} />
                </div>
            </main>
        </div>
    )
}

Is there temporary workaround? Instead of using revalidate, can something else be used to trigger full page regeneration instead of ISR.

azmathmoosa avatar Jul 03 '24 04:07 azmathmoosa

Hi team! I'm also facing the same issue using ISR with app router for our nextjs application. Is there any update about this, or estimated time when this issue will be fixed?

As Quramy mentioned, I've also have the same error log (read-only filesystem) and the run.sh seems only to be checking the pages router, but not the app router.

Hope this can be fixed soon!

Thanks!

grmnlxndr avatar Jul 19 '24 20:07 grmnlxndr

We are working on a fairly large project. Can we get an update or at least confirmation that the team is working on it?

ferbiy avatar Jul 22 '24 14:07 ferbiy

I have the same log output .

Failed to update prerender cache for /index [Error: EROFS: read-only file system, open '/tmp/app/.next/server/app/index.html']

And it seems this issue occurs only for App Router pages.

I downloaded the build artifact and checked compute/run.sh. It seemas that Amplify Hosting creates the directory .next/server/pages as special.

#!/bin/bash
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_SESSION_TOKEN

mkdir -p '/tmp/app/.next/server/pages'
echo '[x-amplify-log][INFO] starting copy'
cp -r '/var/task/.next/server/pages' '/tmp/app/.next/server'
echo '[x-amplify-log][INFO] copy server pages complete'

cp -f /var/task/server.js /tmp/app/server.js
echo '[x-amplify-log][INFO] symlink creation complete path=.next'
ln -sf '/var/task/.next/server/app' '/tmp/app/.next/server/app'
# other sym links

node /tmp/app/server.js

Full run.sh Next.js App Router stores cache files under not .next/server/pages but .next/server/app. So I think this run.sh should not use symlink via ln -sf '/var/task/.next/server/app' '/tmp/app/.next/server/app' but create dir like cp -r '/var/task/.next/server/app' '/tmp/app/.next/server' .

Additional info

  • Next.js ver: 14.2.2
  • GitHub Repo: https://github.com/Quramy/next-js-amplify-study
  • My Amplify app id: d25rciaxjd9k8z
  • Build artifact: https://aws-amplify-prod-us-east-1-artifacts.s3.us-east-1.amazonaws.com/d25rciaxjd9k8z/main/0000000002/BUILD/artifacts.zip

Seeing your answer, I ended up having an idea, if permission is really the problem, what happens if I put Next to write the cache inside the "pages" folder then? I created a CacheHandler here, extending the same Next pattern, but adding an option to change the location of the page cache.

cache-handler.mjs

import FileSystemCache from 'next/dist/server/lib/incremental-cache/file-system-cache.js'
import path from 'path'

class CacheHandler extends FileSystemCache.default {
  constructor(ctx) {
    super(ctx)
  }

  getFilePath(pathname, kind) {
    if (kind === 'app') {
      return path.join(this.serverDistDir, 'pages', 'app', pathname)
    }

    return super.getFilePath(pathname, kind)
  }
}

export default CacheHandler

next.config.mjs

export default {
    cacheHandler: require.resolve('./cache-handler.mjs')
}

I followed the documentation here: https://nextjs.org/docs/app/api-reference/next-config-js/incrementalCacheHandlerPath.

And apparently it solved the problem here, it's not ideal, but as the solution was already said here and the amplify team didn't even move, I think it can solve some people's problems.

alanzdr avatar Aug 02 '24 19:08 alanzdr