Skip to content

feat!: add Node.js 24 support and remove Node.js 20 - #565

Merged
AndreLars merged 5 commits into
mainfrom
fna-1642-node24-engines
Sep 21, 2026
Merged

AndreLars merged 5 commits into
mainfrom
fna-1642-node24-engines

Conversation

@AndreLars

@AndreLars AndreLars commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #563 · FNA-1642 · epic FNA-947 Node20 Deprecation

Warning

BREAKING CHANGE. Projects using create-twilio-function, @twilio-labs/plugin-assets, @twilio-labs/plugin-serverless, @twilio-labs/serverless-twilio-runtime or twilio-run will have to migrate to Node.js 22 or 24.

Problem

Three facets of the same inconsistency:

  1. Node 24 was never declared. feat: add node24 CLI runtime support #555 added node24 as a deploy runtime and made it the default (packages/twilio-run/src/templating/defaultConfig.ts), and added 24. to the local version check, but it never touched any engines field. The toolkit defaulted to deploying to node24 while refusing to declare support for running on Node 24, so Node 24 users saw engine warnings ([FEATURE] [twilio-run] NodeJS 24 support #563).
  2. Node 20 is deprecated on Twilio Serverless and can no longer be used for new builds, but it was still advertised as supported.
  3. Existing projects get no warning. The node24 default only applies to newly generated config files. A project whose .twilioserverlessrc pins "runtime": "node20" keeps sending node20 — serverless-api/src/api/builds.ts forwards whatever is configured — and the build is rejected at the platform with no local hint as to why.

Change

engines.node goes from ^20.x || ^22.x to ^22.x || ^24.x.

Area File
engines in the four published packages packages/{twilio-run,create-twilio-function,plugin-serverless,plugin-assets}/package.json
Scaffolded project's engines range packages/create-twilio-function/src/create-twilio-function/versions.js
New: deploy-time runtime check packages/twilio-run/src/checks/deprecated-runtime.ts, wired into src/commands/deploy.ts
Local Node version check packages/twilio-run/src/checks/nodejs-version.ts
Contributor toolchain .nvmrc → 22, docs/CONTRIBUTING.md
Docs packages/serverless-twilio-runtime/Readme.md
Release workflow .github/workflows/on-merge-main.yml → Node 22

The Node version work mirrors the Node.js 18 removal in #544 — same file set, same major changeset shape, including @twilio-labs/serverless-twilio-runtime so the Node support policy stays aligned across the packages.

#563 named only twilio-run, but the plugins depend on it, so all packages move together rather than leaving plugin-serverless and plugin-assets behind.

Deploy-time warning

checkForDeprecatedRuntime runs during twilio-run deploy and warns when the configured runtime is a deprecated one:

▸ Deprecated Node.js Runtime Configured

Your project is configured to deploy to "node20", which is deprecated and is
no longer accepted for new builds.

Please update the "runtime" field in your .twilioserverlessrc (or pass the
--runtime flag) to a version that is still supported.

Deliberately a warning, not a failure: Twilio Serverless stays the authority on which runtimes it accepts, so the CLI surfaces the problem earlier without pre-emptively blocking a deploy.

Deny list, not allow list

Per review feedback from @alfrol and @jannoteelem — an allow list of supported runtimes baked into the CLI diverges from the platform as soon as a new runtime is added. A project pinned to an older toolkit would tell users their perfectly valid node26 config "is not supported", i.e. the client confidently contradicting the platform. Both checks therefore use one shared deny list of deprecated versions:

export const DEPRECATED_NODE_MAJORS = [4, 6, 8, 10, 12, 14, 16, 18, 20];
export const DEPRECATED_RUNTIMES = DEPRECATED_NODE_MAJORS.map(m => `node${m}`);
stale toolkit, scenario allow list deny list (this PR)
new runtime added (node26) false warning on a valid config correctly silent
new deprecation added correctly silent no warning — platform still rejects the build

A deprecation is a settled fact, so an outdated deny list stays correct; its worst case is a missing hint, where the allow list's worst case is a false claim. The supported set, by contrast, only ever grows, so an allow list would need a release per Node version forever.

Two consequences worth calling out for reviewers:

  • nodejs-version.ts had the same defect before this PR (SERVERLESS_NODE_JS_VERSION was an allow list, so a stale toolkit would warn at anyone on local Node 26). It now compares the local major against the same shared deny list. As a result it no longer warns on odd/non-LTS versions such as 19, 21 or 23 — an intentional trade for not being wrong about future versions.
  • printVersionWarning drops its now-unused expectedVersion parameter. It is not exported from the package's main/types entry point (src/index.ts only exports handlerToExpressRoute and runDevServer) and no package in the monorepo imports it, so this is only reachable via an unsupported deep import — and this is a major release.

Unknown or unrecognised runtime values are now left entirely to the platform, rather than the CLI guessing.

Node 24 is already the default runtime (DEFAULT_RUNTIME = 'node24', written as an active line into generated .twilioserverlessrc files), so no change was needed there.

Testing

  • npx jest packages/twilio-run — 207 passed, 25 suites, 4 snapshots
  • npx jest packages/create-twilio-function — 61 passed, 9 suites
  • npx tsc --noEmit in packages/twilio-run — clean
  • New __tests__/checks/deprecated-runtime.test.ts — every deprecated runtime warns, the list is pinned to the expected even majors, supported runtimes stay silent, future runtimes (node26/node28/node30) stay silent, unset and unrecognised values stay silent, and the check never exits the process
  • New __tests__/checks/nodejs-version.test.ts — this check previously had no test coverage at all; now covers deprecated local versions warning, supported ones staying silent, and future ones (26/28) staying silent
  • npx changeset status --since=origin/main — major bump for exactly the five intended packages, nothing at patch/minor
  • Swept the repo for remaining Node 20 references; the only hits left are historical CHANGELOG.md entries

The test matrix in nodejs.yml is [lts/-1, lts/*], which tracks 22 and 24 — no matrix change needed.

🤖 Generated with Claude Code

FNA-1642

`node24` was added as a deploy runtime and made the default in #555, and
the local Node version check already accepts Node 24, but `engines.node`
was left at `^20.x || ^22.x`. The toolkit therefore defaulted to
deploying to node24 while refusing to declare support for running on
Node 24, so users on Node 24 saw engine warnings.

Widen the range to `^20.x || ^22.x || ^24.x` across the four published
packages. The plugins depend on `twilio-run`, so bumping only
`twilio-run` would leave them blocking Node 24 — all four move together.

Fixes #563.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Sep 17, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5962840

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@twilio-labs/serverless-twilio-runtime Major
create-twilio-function Major
@twilio-labs/plugin-serverless Major
@twilio-labs/plugin-assets Major
twilio-run Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

FNA-1642

Node.js 20 is deprecated on Twilio Serverless and can no longer be used
for new builds, so drop it from the supported range in the same change
that adds Node.js 24.

- `engines.node` becomes `^22.x || ^24.x` in the four published packages
- the scaffolded project template's engines range follows suit
- the local version check no longer accepts Node 20
- `.nvmrc`, CONTRIBUTING and the serverless-twilio-runtime Readme move to
  the new minimum, and the on-merge-main workflow builds on Node 22

Mirrors the Node.js 18 removal in #544.

BREAKING CHANGE: Projects using create-twilio-function, plugin-assets,
plugin-serverless, serverless-twilio-runtime or twilio-run will have to
migrate to Node.js 22 or 24.

Fixes #563.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@AndreLars AndreLars changed the title feat: declare Node.js 24 support in engines feat!: add Node.js 24 support and remove Node.js 20 Sep 17, 2026
FNA-1642

Removing node20 from the supported range does not help existing projects
whose .twilioserverlessrc still pins "runtime": "node20" — the CLI keeps
sending it and the build fails at the platform with no local hint as to
why.

Add a `checkForDeprecatedRuntime` check, run from `twilio-run deploy`,
that warns when the resolved runtime is not one of node22/node24 and
points at the config field to change. It warns rather than fails: the
platform remains the authority on which runtimes it accepts, and an
unset runtime stays silent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* supported by Twilio Serverless. Deprecated runtimes are rejected for new
* builds, so this surfaces the problem before the deploy fails.
*/
export function checkForDeprecatedRuntime(runtime?: string): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't there be a risk of divergence if someone is using toolkit programmatically and doesn't update to the latest version where supported runtimes are changed?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sounds like it should be checked server-side?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g. when we add node26, the toolkit will tell users their valid runtime "is not supported" Worth double-checking this scenario.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can invert and use a denylist instead of allowlist, we don't need to keep it updated in order to allow new runtimes, if a runtime is deprecated it will be checked server-side anyways, so keeping it updated is less critical

AndreLars and others added 2 commits September 18, 2026 14:35
FNA-1642

Review feedback: an allow list of supported runtimes baked into the CLI
diverges from the platform as soon as a new runtime is added. A project
pinned to an older toolkit would tell users their valid node26 config
"is not supported" — the client confidently contradicting the platform.

Invert both checks to deny lists of deprecated versions. A deprecation
is a settled fact, so an outdated deny list stays correct: at worst it
misses a newer deprecation and the platform rejects the build as it
would have anyway. Unknown and future values are left alone.

- `deprecated-runtime.ts` lists every even major up to 20 and no longer
  warns on unrecognised runtime values
- `nodejs-version.ts` compares the local major against the same list
  instead of an allow list of supported versions
- `printVersionWarning` drops its now-unused expectedVersion parameter
- add the missing test coverage for `checkNodejsVersion`

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
FNA-1642

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jannoteelem

Copy link
Copy Markdown
Contributor

DEPRECATED_NODE_MAJORS works for config.runtime (Twilio's runtime set is even-only), but checkNodejsVersion reads process.versions.node, which can be any major. Odd ones now fall through:

Local Node Before Now
20.11.1 warns warns
21.7.3 warns silent
23.11.0 warns silent
25.1.0 warns silent
26.0.0 warns silent (intended)

Odd majors are Current-only and EOL in ~8 months, but people stay on them (nvm install node during an odd window, a pinned image, a sibling .nvmrc). Node 25 is the live case. They get no local/production mismatch warning, and never will: these were never Twilio runtimes, so they won't be added to this list as they EOL.

A floor fits the local check better:

const LOWEST_SUPPORTED_NODE_MAJOR = 22;
if (major < LOWEST_SUPPORTED_NODE_MAJOR) {
  printVersionWarning(nodeVersion);
}

No gaps, still silent on newer versions, one number to maintain. Worth adding 21.7.3 and 23.11.0 to nodejs-version.test.ts, since the current cases are all even and pass either way.

@AndreLars
AndreLars merged commit ee4cf89 into main Sep 21, 2026
14 checks passed
@AndreLars
AndreLars deleted the fna-1642-node24-engines branch September 21, 2026 09:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] [twilio-run] NodeJS 24 support

4 participants