Skip to content

Conversation

@DBosley
Copy link

@DBosley DBosley commented Nov 21, 2025

What?

Adds null checks for globalThis.openNextConfig in the cache handler to prevent TypeError during Next.js 16 build phase.

Why?

When using the Next.js 16 Adapters API, the cache handler class is instantiated during SSG/prerendering before globalThis.openNextConfig is initialized by the runtime handlers. This causes:

TypeError: Cannot read properties of undefined (reading 'dangerous')

How?

Changed all occurrences of:

if (globalThis.openNextConfig.dangerous?.disableIncrementalCache)

To:

if (globalThis.openNextConfig?.dangerous?.disableIncrementalCache)

Fixed 4 locations in packages/open-next/src/adapters/cache.ts:

  • get() method (line 48)
  • set() method (line 207)
  • revalidateTag() method (line 325)
  • updateTagsOnSet() method (line 433)

Testing

  • Added 3 test cases that verify cache operations work when globalThis.openNextConfig is undefined
  • All 50 existing cache tests continue to pass

Type of Change

  • Bug fix (non-breaking change that fixes an issue)

Related Issues

This is a blocker for the Adapters API work in opennextjs-cloudflare.

Adds optional chaining when accessing globalThis.openNextConfig to prevent
TypeError during Next.js 16 build phase when using the Adapters API. The cache
handler can be instantiated during SSG/prerendering before openNextConfig is
initialized by the runtime handlers.
@changeset-bot
Copy link

changeset-bot bot commented Nov 21, 2025

🦋 Changeset detected

Latest commit: afe0d93

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

This PR includes changesets to release 2 packages
Name Type
@opennextjs/aws Patch
app-pages-router 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

@vicb
Copy link
Contributor

vicb commented Nov 21, 2025

Is this PR AI generated?

@DBosley
Copy link
Author

DBosley commented Nov 21, 2025

Is this PR AI generated?

@vicb
I used claude to help me write the PR and to create a comprehensive description, but this wasn't done in an automated way if that's what you're asking. I'm trying to assist with the adapters-api changes, but ran into errors that stemmed from the above issue. This was the solution that seemed to work. I've got it linked to the cloudflare repo locally and this fix is working to get the adapters-api functional

Is the usage of AI tooling a problem? The goal is to be comprehensive in the tests, documentation, PR description, and completeness of the solution.

@khuezy
Copy link
Contributor

khuezy commented Nov 21, 2025

I won't speak on behalf of the cloudflare side, but on the aws side, there's nothing wrong w/ using AI. Thanks for the PR!

@khuezy
Copy link
Contributor

khuezy commented Nov 21, 2025

Might be worth investigating if we can move the instantiation of the openextConfig before so it's available at build time to avoid the type checking. I believe we had a similar issue a few months ago where we fixed some out of order function calls.

@DBosley
Copy link
Author

DBosley commented Nov 21, 2025

Might be worth investigating if we can move the instantiation of the openextConfig before so it's available at build time to avoid the type checking. I believe we had a similar issue a few months ago where we fixed some out of order function calls.

I can take a look to see if that's a possibility instead of just null checking, if it's possible I'll close this and open a new PR

@DBosley
Copy link
Author

DBosley commented Nov 21, 2025

I dug into this to see if we could reorder things to avoid the optional chaining. Unfortunately it's not possible due to how ES module evaluation works.

The issue is that util.ts has a top-level const nextServer = new NextServer.default({...}) which instantiates Next.js immediately during module evaluation. And requestHandler.ts imports from util.ts, which gets imported by createMainHandler.ts, which gets imported by server-adapter.ts. So the entire import chain resolves synchronously before we even get to the await createMainHandler() call.

The config is set inside createMainHandler() using await import("./open-next.config.mjs"), which is async. By the time that async import completes and sets globalThis.openNextConfig, Next.js has already been instantiated and the cache handler has already been loaded.

I traced through the execution order:

  1. Module graph resolves (synchronous) - all imports walk the dependency tree
  2. Module code executes (synchronous) - util.ts creates Next.js server, which loads cache handler
  3. Top-level await (async) - createMainHandler() runs and sets the config

So the cache class is defined during step 2, but the config isn't available until step 3.

The only alternatives would be build-time injection (loses runtime config flexibility, and I found an earlier commit where this concept was actually removed 1981a47d) or lazy Next.js initialization (substantial refactoring, would need to defer instantiation until first request). Both seem like worse solutions.

The only time the null chaining is needed is during the brief module evaluation phase at cold start. After that the config is guaranteed to be set before any requests are handled.

@khuezy
Copy link
Contributor

khuezy commented Nov 22, 2025

Ah ok, thanks for the investigation.

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.

3 participants