Skip to content

Conversation

@dulapahv
Copy link

Next.js 16.1.0 introduced a new internal module (fast-set-immediate.external.js) that attempts to assign to read-only module exports during the esbuild bundling process. This causes a startup crash on Cloudflare Workers with the error:

TypeError: Cannot assign to read only property 'setImmediate' of object '[object Module]'

Fixes #1049

Changes

  • Created an esbuild plugin that intercepts the fast-set-immediate.external module and replaces it with a shim.

Verified by building the playground16 example project with Next.js 16.1.0. Shimmed exports exist in the bundle at .open-next/server-functions/default/examples/playground16/handler.mjs and this error no longer occur.

Copilot AI review requested due to automatic review settings December 20, 2025 00:45
@changeset-bot
Copy link

changeset-bot bot commented Dec 20, 2025

🦋 Changeset detected

Latest commit: b04debb

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

This PR includes changesets to release 1 package
Name Type
@opennextjs/cloudflare Patch

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

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds compatibility for Next.js 16.1.0 by introducing an esbuild plugin that shims the problematic fast-set-immediate.external module, which attempts to assign to read-only exports during bundling and causes startup crashes on Cloudflare Workers.

  • Created a new esbuild plugin to intercept and replace the fast-set-immediate.external module with a shim
  • Integrated the plugin into the server bundling process

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
packages/cloudflare/src/cli/build/patches/plugins/fast-set-immediate.ts New esbuild plugin that intercepts the fast-set-immediate module and provides shimmed exports to prevent read-only property assignment errors
packages/cloudflare/src/cli/build/bundle-server.ts Imports and registers the new fast-set-immediate shim plugin in the esbuild plugins array
.changeset/deep-breads-camp.md Changeset documentation describing the fix for Next.js 16.1.0 compatibility

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@vicb
Copy link
Contributor

vicb commented Dec 20, 2025

Great finding @dulapahv !
I think we need to understand why Next does patch setImmediate before merging this and if this PR will have side effects.

Note that cloudflare/workerd#5662 should help here too

@dulapahv
Copy link
Author

Thanks for the feedback. From what I understand is that the setImmediate patch (in this PR) changes the usual behavior of the event loop by making setImmediate run immediately after the current task instead of waiting for the next event loop iteration.

Next.js's fast-set-immediate.external.ts runs this at module initialization:

function install() {
  if (process.env.NEXT_RUNTIME === 'edge') {
    // Nothing to patch. The exported functions all error if used in the edge runtime,
    // so we're not going to violate any assumptions by not patching.
    return
  } else {
    debug?.('installing fast setImmediate patch')

    const nodeTimers = require('node:timers') as typeof import('node:timers')

    // ⚠️ THIS LINE FAILS WHEN BUNDLED TO ESM
    globalThis.setImmediate = nodeTimers.setImmediate =
      // Workaround for missing __promisify__ which is not a real property
      patchedSetImmediate as unknown as typeof setImmediate
    globalThis.clearImmediate = nodeTimers.clearImmediate =
      patchedClearImmediate

    const nodeTimersPromises =
      require('node:timers/promises') as typeof import('node:timers/promises')
    nodeTimersPromises.setImmediate =
      patchedSetImmediatePromise as typeof import('node:timers/promises').setImmediate

    process.nextTick = patchedNextTick
  }
}

// Called at module load time
install()

The problem is this part:

globalThis.setImmediate = nodeTimers.setImmediate = patchedSetImmediate

This works in Node.js with CommonJS because require('node:timers') returns a mutable object. However, when esbuild bundles this to ESM, node:timers becomes an ES module namespace object with immutable exports. The assignment to nodeTimers.setImmediate throws:

TypeError: Cannot assign to read only property 'setImmediate' of object '[object Module]'

This happens at module initialization time when install() is called, so the Worker crashed before any request is handled.

I don't fully understand what Next.js relies on this patch for beyond the timing optimization described in this PR. I can't confidently say there are no side effects from shimming this out.

@ZiedYousfi
Copy link

Maybe until this PR is merged you could say it in the documentation. I'm sorry if I'm bothering but I just spent half my day trying to debug this just to find this PR.

@vicb
Copy link
Contributor

vicb commented Jan 5, 2026

Maybe until this PR is merged you could say it in the documentation. I'm sorry if I'm bothering but I just spent half my day trying to debug this just to find this PR.

The problem is that a lot of users don't read the docs https://opennext.js.org/cloudflare#supported-nextjs-versions ;)

@ZiedYousfi
Copy link

Maybe until this PR is merged you could say it in the documentation. I'm sorry if I'm bothering but I just spent half my day trying to debug this just to find this PR.

The problem is that a lot of users don't read the docs https://opennext.js.org/cloudflare#supported-nextjs-versions ;)

Ok my bad but maybe you can put a link to the issue page as I think it could be very relevant for this specific project because Next.js is very easy to pinpoint what the known problem are?

@dulapahv
Copy link
Author

dulapahv commented Jan 5, 2026

Given that the root cause needs to be addressed at the runtime layer and that #1057 implements a proper fix, this shim approach should probably not be merged. Happy to close this in favor of #1057.

@vicb
Copy link
Contributor

vicb commented Jan 5, 2026

Given that the root cause needs to be addressed at the runtime layer and that #1057 implements a proper fix, this shim approach should probably not be merged. Happy to close this in favor of #1057.

Yep I think this can be close.
There are still issues with the other PRs and Next 16.1 but I'll take a look tomorrow.
Thanks!

@dulapahv dulapahv closed this Jan 5, 2026
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.

[BUG] Next.js 16.1.0 crashes on Cloudflare Workers (Error 1101): Cannot assign to read only property 'setImmediate' of [object Module]

3 participants