Fix: sh: 1: next: not found on Heroku
Three different, unrelated situations produce the exact same Heroku crash — here is how to tell which one is yours before you change anything.
TL;DR#
sh: 1: next: not found means the dyno that is running right now has no next binary in node_modules/.bin — it does not mean your local package.json is wrong. Three unrelated situations put a Heroku dyno in that state: next was missing, or only listed under devDependencies, in the commit Heroku actually built; the fix exists on your machine but was never pushed to the branch Heroku deploys from; or the dyno is still running an older release, because only a code deploy produces a new build — a config-var or add-on change, or a rollback, creates a release on top of an old, unrebuilt slug, and a plain restart deploys nothing at all. heroku releases tells you which one you are dealing with before you touch any code.
The error#
The Stack Overflow question behind this page shows a Next.js app that "runs fine locally" but crashes on the Heroku dyno:
2021-08-13T07:26:20.752659+00:00 app[web.1]:
2021-08-13T07:26:20.752685+00:00 app[web.1]: > @ start /app
2021-08-13T07:26:20.752688+00:00 app[web.1]: > next start -p $PORT
2021-08-13T07:26:20.752689+00:00 app[web.1]:
2021-08-13T07:26:20.763505+00:00 app[web.1]: sh: 1: next: not found
2021-08-13T07:26:20.769117+00:00 app[web.1]: npm ERR! code ELIFECYCLE
2021-08-13T07:26:20.769496+00:00 app[web.1]: npm ERR! syscall spawn
2021-08-13T07:26:20.769568+00:00 app[web.1]: npm ERR! file sh
2021-08-13T07:26:20.769636+00:00 app[web.1]: npm ERR! errno ENOENT
2021-08-13T07:26:20.772506+00:00 app[web.1]: npm ERR! @ start: `next start -p $PORT`
2021-08-13T07:26:20.772533+00:00 app[web.1]: npm ERR! spawn ENOENTThe asker's package.json already lists next under dependencies, not devDependencies:
{
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start -p $PORT"
},
"dependencies": {
"@heroicons/react": "^1.0.3",
"next": "latest",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"tailwind-scrollbar-hide": "^1.0.3"
},
"devDependencies": {
"autoprefixer": "^10.2.6",
"postcss": "^8.3.5",
"tailwindcss": "^2.2.4"
}
}That detail matters: it rules out one widely-repeated piece of advice for this error (move the package out of devDependencies) for this specific file, and it is why the fix here is a short checklist rather than one instruction.
Why it happens#
npm start runs the start script in a subshell, and that subshell's PATH is extended with node_modules/.bin before the script's own command is looked up — npm's own documentation describes this as adding node_modules/.bin "to the PATH provided to scripts," with binaries resolved "without the node_modules/.bin prefix." next only exists at that path because the next package was installed and its bin entry was linked there. sh: 1: next: not found is the shell reporting that the lookup failed — full stop. Everything else is about why it failed on this particular dyno, and three different, independent situations produce exactly the same message:
nextwas not actually installed by the build Heroku ran — or it was installed, then removed. Heroku's Node.js buildpack documentation states that "by default, Heroku installs all dependencies listed inpackage.jsonunderdependenciesanddevDependencies," and separately that "after running the installation, Heroku prunes out the packages declared underdevDependenciesbefore deploying the application" (the same page documents a way to skip this pruning step). If a package used byscripts.startsits only indevDependencies, it is gone by the time the web dyno runsnpm start— the exact mechanism (and a full reproduction against the real buildpack) is covered on thebabel-node: not foundpage. For a default Next.js project this is less likely: pinningnext@15and installing it (shown reproduced below) wrote the package straight intodependencies, matching the asker's ownpackage.jsonabove — but it is still worth a direct check on whatever commit Heroku actually built, not on your local file.- The fix was never pushed to the branch Heroku builds. Heroku's git deployment documentation is explicit that you "push the code from your local repository's main branch to your
herokuremote," and shows the equivalent for any other local branch name:git push heroku testbranch:main— and the same page points to a separate GitHub integration as the alternative way to deploy. Pushing only to GitHub, or to a differently-named local branch, changes nothing on the dyno unless that GitHub integration is the thing actually deploying it. - The dyno is simply running an older release. Heroku's releases documentation lists what creates a new release: deploying code, changing a config var, modifying an add-on, or rolling back to a previous one. Only a code deploy is a new build — it is what produces a new slug, which Heroku describes as "a bundle of your source, fetched dependencies, the language runtime, and compiled/generated output of the build system, ready for execution." A config-var or add-on change creates a release on the same, unrebuilt slug; a rollback explicitly "copies the compiled slug … of the release you roll back to," per the same releases documentation — reusing an old one, not building a new one. Restarting is not in this list at all: Heroku's dynos documentation says each dyno "gets its own ephemeral filesystem, with a fresh copy of the most recently deployed code." A restart — automatic or manual — gives the dyno a new filesystem, not new code: it is still a fresh copy of whatever was last actually deployed, not a rebuild. If
nextwas added or fixed after the last real code push, nothing short of another push puts it on the dyno.
The Stack Overflow thread never shows the build log for the asker's own deploy, so which of these actually happened for them cannot be determined from the evidence — the next section is a checklist that works out which one applies to your own case instead of guessing.
Fix#
- Find out what is actually running before changing anything:
heroku releases
heroku releases:info v24Heroku's releases documentation shows what these print: heroku releases lists a Change column where a real deploy reads Deploy <commit hash> and a config change reads Config add <VAR_NAME>; heroku releases:info v24 expands release v24 specifically into Change:, Addons: and Config: lines — name the release you actually want to inspect. Scan down to the most recent Deploy <hash> row — a Config add … row on top of it does not change the code, but a rollback row does, to whichever older slug it names — then compare that hash against the commit where you added or fixed next.
- Inspect the deployed code directly, rather than trusting your local file. Heroku's one-off dynos documentation says a one-off dyno "can make full use of anything deployed in the application":
heroku run cat package.json
heroku run ls node_modules/.binIf next is missing from the second command's output, the binary is not there right now on whatever was last deployed — the question becomes why, which the next two steps answer.
- If
nextis missing from that output, add it — this also installs it straight intodependencies, no flag needed, because npm only falls back todependencieswhen the package is not already listed anywhere:
npm install nextThis is exactly what the original question's top answer did, and it is the one-line fix when the package is genuinely absent. Reproduced on 26 October 2026 in a throwaway node:22-slim container (Node 22.23.3, npm 10.9.9): a package.json listing only react and react-dom, with a start script of next start -p 3000, gives this on npm start:
> start
> next start -p 3000
sh: 1: next: not foundThe one change — pinning and installing next@15, which resolved to 15.5.28 — is enough to make the binary resolve — node_modules/.bin then lists next alongside its own transitive binaries (nanoid, semver).
- If
nextis already listed, but only underdevDependencies, move it instead of reinstalling it. A barenpm install nextleaves it exactly where it already is — npm keeps a package's existing dependency type and only defaults todependencieswhen the package is not listed anywhere yet. Reproduced on 26 October 2026, samenode:22-slimimage: starting fromdevDependencies: { "next": "^15.0.0" }and runningnpm install nextresolved the version in place but left it underdevDependencies. The flag that actually moves it is--save-prod(or-P):
npm install next --save-prod- If
nextis already correct in the deployed commit, the release you are running is simply stale. Push again, explicitly to the branch Heroku builds:
git push heroku mainChanging a config var will not do this instead — it creates a release, but on the same slug, with no rebuild (Heroku's releases documentation). Running heroku restart will not do it either: it gives the dyno a fresh copy of whatever was most recently deployed, per Heroku's dynos documentation — the same code, not new code.
Verify the fix#
heroku logs --tailConfirm sh: 1: next: not found no longer appears, and that heroku releases now shows a fresh release built from the commit with the fix — not just a bumped release number from a config change.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
The error you will see next#
Once next resolves, a project with no completed production build hits a different, well-documented error. In the same reproduction above, after npm install next, running npm start again no longer reports "not found" — it starts, then fails with:
> start
> next start -p 3000
▲ Next.js 15.5.28
- Local: http://localhost:3000
✓ Starting...
[Error: Could not find a production build in the '.next' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id]That is progress, not a new problem: the shell found and ran next, which is what this page is about. On Heroku, the buildpack documentation cited above says a build script in package.json runs automatically during deploy — unless a heroku-postbuild script is defined, which then runs instead of it. Making sure that step actually ran, then running next build before next start on any platform, closes the gap; the dedicated page for this exact message covers the remaining ways a production build can go missing even when the next binary itself is fine.
The same shell error, other platforms#
The underlying mechanism — an npm script that calls a command whose package was never actually in node_modules at run time — is not specific to Heroku or to next. The identical sh: 1: <command>: not found shape shows up compiling TypeScript inside a Docker build, almost always for the same reason: a devDependency skipped by a production-only install step, covered in the tsc: not found in Docker fix. A different-looking but unrelated failure is next failing to load its native SWC binary: there the next command itself is present and starts, but a platform-specific compiler binary inside @next/swc is missing because node_modules was installed for a different OS or CPU architecture — worth knowing apart from this error, since the fix (a clean reinstall on the target platform) is nothing like a package.json or deploy problem.
Whichever of the three situations above explains your case, the pattern is the same: whatever npm start calls has to exist in node_modules on the release that is actually running, not on your laptop and not on an older release still being served under a newer release number.
Further reading: Fix babel-node: not found on Heroku (devDependencies pruned).
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
Fix Next.js missing .next/BUILD_ID at server startup
Separate an absent build from the wrong application root, a mismatched output folder and development artifacts, using the file the production server checks.
Next.js "Failed to load SWC binary": the real fix
Why next dev or next build refuses to start, what actually happens when npm cannot find the right @next/swc binary, and the one reinstall that puts it back — reproduced on Next.js 16.4.2.
Next.js 16 /_not-found Prerender Build Error: The Real Fix
Next.js 16 build crashes prerendering the internal /_not-found route. Here are the three actual causes behind it, how to tell which one is yours, and the fix for each.


