How to Fix: Error: Failed to find Server Action “84c…c2”. This request might be from an older or newer deployment. Original error: Cannot read properties of undefined (reading ‘workers’)
This error is usually not a random Server Action failure. It is a deployment mismatch: the browser submits a request containing a previously generated Server Action identifier, but the running server no longer recognizes that identifier. In the Heroku repro, the mismatch is amplified by how the app is built and served across dyno restarts or release transitions, which can surface as Failed to find Server Action followed by the lower-level runtime error Cannot read properties of undefined (reading ‘workers’).
Understanding the Root Cause
In Next.js Server Actions, each action is compiled into the build output and associated with an internal identifier. When a page is rendered, forms and client-side calls reference that identifier. If a user loads HTML or JavaScript from one deployment, then submits the action against a different deployment, the server may not be able to resolve the action anymore.
That is exactly what the message means:
Error: Failed to find Server Action "84c...c2". This request might be from an older or newer deployment.
The important part is older or newer deployment. The action ID is tied to a specific build. On platforms like Heroku, this can happen when:
- A new slug is released while a user still has an older page open.
- Static assets or the HTML response are cached longer than the server process lifecycle.
- Multiple dynos or release phases briefly serve inconsistent build artifacts.
- The app rebuilds in a way that changes the Server Action manifest between requests.
The second error:
Cannot read properties of undefined (reading 'workers')
is typically a downstream runtime failure, not the original cause. Once Next.js cannot resolve the action from its manifest/runtime mapping, internals may attempt to access request handling state that is no longer valid in that code path. In practice, you should treat the missing Server Action as the real problem to solve.
Technically, the fix is not to “recover” the missing action ID. The fix is to ensure the browser and the server are using the same build output, and to reduce scenarios where clients can submit actions generated by a different deployment.
Step-by-Step Solution
The most reliable solution on Heroku is to make deployment behavior predictable, avoid stale action references, and ensure the runtime serves a single consistent .next build.
1. Build once, run that exact build
Make sure Heroku creates the production build during slug compilation and then starts the app from that same artifact.
{
"scripts": {
"build": "next build",
"start": "next start -p $PORT"
}
}
Your Procfile should be simple:
web: npm run start
This avoids custom runtime behavior that could rebuild, partially rebuild, or start from inconsistent output.
2. Use a stable Node.js version supported by your Next.js release
Pin the runtime in package.json so local, CI, and Heroku builds do not produce different server bundles.
{
"engines": {
"node": "20.x"
}
}
If the repro uses an older stack or mismatched Node version, update it and redeploy.
3. Disable accidental caching of dynamic HTML responses
If the page containing the Server Action is rendered dynamically, do not let intermediaries keep old HTML around after a deployment.
In an App Router page or layout that depends on actions, force dynamic rendering when appropriate:
export const dynamic = 'force-dynamic';
For route handlers returning HTML or action-related responses, add explicit no-store headers where relevant:
import { NextResponse } from 'next/server'
export async function GET() {
return new NextResponse('ok', {
headers: {
'Cache-Control': 'no-store, max-age=0'
}
})
}
This does not eliminate all deployment race conditions, but it significantly reduces the chance that a user submits a form generated by an outdated build.
4. Avoid long-lived open pages during deploy-sensitive flows
Server Actions are more sensitive than plain POST endpoints because they depend on generated IDs. If users keep forms open for a long time, then a deployment happens, those forms may submit stale identifiers.
For critical flows, prefer one of these patterns:
- Reload the page before submission-sensitive steps.
- Use traditional Route Handlers or API endpoints for very long-lived forms.
- Show a “new version available” banner after deploy and prompt refresh.
A lightweight client refresh pattern:
'use client'
import { useEffect } from 'react'
import { useRouter } from 'next/navigation'
export function RefreshOnVersionChange({ version }) {
const router = useRouter()
useEffect(() => {
const current = localStorage.getItem('app-version')
if (current && current !== version) {
router.refresh()
}
localStorage.setItem('app-version', version)
}, [router, version])
return null
}
Render it with a version derived from the current release, such as a commit SHA or Heroku release identifier.
5. Ensure all dynos run the same release
If you scale horizontally, verify that every web dyno is serving the same slug and that there is no custom process mutating build output at runtime. Heroku normally manages this well, but custom scripts, shared writable directories, or startup-time generation can cause drift.
Keep the filesystem assumptions simple:
- Do not write into .next at runtime.
- Do not rebuild on dyno boot.
- Do not mix build artifacts from multiple releases.
6. Upgrade Next.js if you are on an affected canary or early Server Actions implementation
The repro issue is tied to internals around Server Actions resolution. If your app runs an early or experimental version, upgrade to a newer stable release where App Router and Server Actions handling is more robust.
npm install next@latest react@latest react-dom@latest
Then rebuild and redeploy:
rm -rf .next node_modules
npm install
npm run build
Push the fresh build path to Heroku:
git add .
git commit -m "Stabilize Server Actions deployment behavior"
git push heroku main
7. If the flow must survive deploys, replace the Server Action with a route endpoint
If users may submit after minutes or hours, a regular endpoint is often a better fit because it does not depend on a build-scoped action ID.
Create a route handler:
import { NextResponse } from 'next/server'
export async function POST(request) {
const formData = await request.formData()
const email = formData.get('email')
return NextResponse.json({ ok: true, email })
}
Submit to it from a form:
<form action="/api/subscribe" method="post">
<input name="email" type="email" required />
<button type="submit">Submit</button>
</form>
This trades some Server Action convenience for stronger deployment tolerance.
8. Verify the fix
Test the exact failure mode:
- Open the page in a browser.
- Deploy a new release to Heroku.
- Submit the old form without refreshing.
If you still use Server Actions, a stale page can still fail by design. The goal is to minimize that window and handle it gracefully. If you need guaranteed success across deployments, use a POST route instead.
You can also add user-facing recovery:
'use client'
export function ActionErrorMessage() {
return (
<p>
If this form stops working after a deployment, refresh the page and try again.
</p>
)
}
Common Edge Cases
Reverse proxy or CDN caching old HTML
If you place Heroku behind a CDN or proxy, the browser may receive stale HTML containing obsolete action IDs even though the origin is updated. Configure dynamic pages with Cache-Control: no-store or a very low TTL.
Users submit forms from multiple tabs
One tab may hold an older deployment while another tab loads the newer one. This creates a classic build mismatch. Refresh-on-version-change logic helps here.
Experimental Next.js or React versions
Early Server Actions support changed quickly. If the issue appears on an experimental release, upgrading is often part of the fix.
Build-time environment drift
If environment variables differ between local, CI, and Heroku, action compilation output can change unexpectedly. Ensure production env values are set before build, not patched afterward.
Custom startup scripts
If your Heroku startup sequence performs extra file generation, modifies server bundles, or conditionally swaps configs, the Server Action manifest can become inconsistent with the rendered client output.
Assuming the ‘workers’ error is the root cause
It usually is not. Focus on why the action ID cannot be resolved first.
FAQ
Why does this happen more often on Heroku than in local development?
Local development usually runs a single process with a continuously updated environment. On Heroku, deployments create new slugs and dyno restarts, so users can more easily interact with HTML from one release and a server from another.
Can I completely prevent this while still using Server Actions?
Not perfectly for long-lived pages. Because Server Actions are tied to the build output, a page that remains open across deployments can become stale. You can reduce the chance with no-store caching and refresh-on-version-change, but route handlers are safer for deploy-resilient submissions.
Should I switch all Server Actions to API routes?
No. Use Server Actions where the interaction is short-lived and tightly coupled to the current rendered UI. Switch to Route Handlers or API endpoints for flows that must survive redeploys, background tabs, delayed submissions, or aggressive caching layers.
For the GitHub repro, the practical fix is to treat the error as a release consistency problem: build once, serve one consistent artifact, avoid stale HTML, upgrade Next.js if needed, and use conventional POST endpoints for forms that may outlive a deployment.