You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A recent change in t3.chat introduced Effect (v3.15.5) into the codebase. We're also using uploadthing, which internally uses Effect 3.14.21 (https://github.com/pingdotgg/uploadthing/blob/091b97bcf8bb604ff2f39c19d6cb310c378eaf38/packages/uploadthing/package.json#L161). Uploadthing does not expose effectful APIs to its consumers, hence it's a dependency and not a peer-dependency (a decision we'll probably revise in the next major version). This leads to a potential of multiple versions of effect being included for a single project. While this has some downsides, this is common for many packages (I bet you can find many versions of the same package if you scroll through your lockfile), and should not cause any issues as libraries like uploadthing should be able to use Effect internally without users knowing or caring what version the library might be using.
When it comes to Effect however, they enforce strict version compliance when executing Effects. An effect created by one version of Effect, must be executed by the same version of Effect:
Effect will in the future log at warning level instead of crashing the program Effect-TS/effect#5064. This reproducer also has this patch applied to not crash the app prematurely
To prevent this, we added pnpm.overrides to force a single version of Effect in the t3chat repo:
Despite this, module caching led the "old version" included by Uploadthing (3.14.21) to be kept around and included in the deployed output causing the app to crash during chat generations.
I was willing to call it a day here, and conclude the issue being cache related. I had several proof of this being the case for t3chat, both locally by running rm -rf node_modules .next after changing branches as well as remotely when redeploying an erroneous deployment without build cache. But this is not the end of it, and the Effect folks went ahead and dug deeper.
The issue at hand
This repo tries to reproduce the issue. While I was not able to reproduce the bad cache behaviors causing the issue with pnpm.overrides enabled, I was able to reproduce the core cause of the issue. If we look at the error message from the original tweet by Theo:
Cannot execute an Effect versioned 3.16.5 with a Runtime of version 3.14.21
This indicates that some bits of UploadThing code executed Effects passed from the application layer. This however, is not the case. No piece of Effect code from the app layer is ever touching the uploadthing layer, so how are Effect's versioned 3.16.5 being executed by Effect 3.14.21?
The reproducer
tl;dr - run pnpm preview
✓ Ready in 227ms
Cannot execute an Effect versioned 3.16.8 with a Runtime of version 3.14.21
Cannot execute an Effect versioned 3.16.8 with a Runtime of version 3.14.21
Cannot execute an Effect versioned 3.16.8 with a Runtime of version 3.14.21
Cannot execute an Effect versioned 3.16.8 with a Runtime of version 3.14.21
Cannot execute an Effect versioned 3.16.8 with a Runtime of version 3.14.21
Cannot execute an Effect versioned 3.16.8 with a Runtime of version 3.14.21
Cannot execute an Effect versioned 3.16.8 with a Runtime of version 3.14.21
Cannot execute an Effect versioned 3.16.8 with a Runtime of version 3.14.21
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
Cannot execute an Effect versioned 3.14.21 with a Runtime of version 3.16.8
timestamp=2025-06-21T17:38:49.762Z level=INFO fiber=#2 message="Got data" data=undefined
pnpm puts transitive dependencies inside a nested node_modules/.pnpm folder:
But what next isn't taking into account here is that we have 2 versions. As soon as the compiler first generates this alias, all future references will use this aliased path no matter where the source is. So the uploadthing library will sometimes get v3.14.5 and sometimes v3.16.8. With normal module resolution algorithms, the uploadthing code should always resolve the 3.14 entrypoints, and the application code should always resolve the 3.16 entrypoints.
#3 applies a patch to remove this alias generation and voila... the mismatched version log goes away!
But what next isn't taking into account here is that we have 2 versions. As soon as the compiler first generates this alias, all future references will use this aliased path no matter where the source is. So the uploadthing library will sometimes get v3.14.5 and sometimes v3.16.8. With normal module resolution algorithms, the uploadthing code should always resolve the 3.14 entrypoints, and the application code should always resolve the 3.16 entrypoints.
This is further amplified by how it only aliases root entrypoints of packages whilst not respecting module exports at all (e.g. 'effect$' only targets exact matches due to the $ suffix). So module exports will be resolved using proper module resolution whilst root entrypoint imports are using whatever alias path was generated. This means that also within a single callsite (importing package) you can get a mix of different versions of a package if you mix together root entrypoint and module imports.
I opened a PR to remove the effect packages from that optimisation list but ideally the Next.js maintainers would come up with a proper fix for this problem instead if it's genuinely a performance concern during development time otherwise.
I'm curious to hear from the next.js maintainers what the intended behavior here is exactly so this can be fixed sustainably.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Repro for https://x.com/thefubhy/status/1936485841871773793
Background
A recent change in t3.chat introduced Effect (v3.15.5) into the codebase. We're also using uploadthing, which internally uses Effect 3.14.21 (https://github.com/pingdotgg/uploadthing/blob/091b97bcf8bb604ff2f39c19d6cb310c378eaf38/packages/uploadthing/package.json#L161). Uploadthing does not expose effectful APIs to its consumers, hence it's a dependency and not a peer-dependency (a decision we'll probably revise in the next major version). This leads to a potential of multiple versions of effect being included for a single project. While this has some downsides, this is common for many packages (I bet you can find many versions of the same package if you scroll through your lockfile), and should not cause any issues as libraries like uploadthing should be able to use Effect internally without users knowing or caring what version the library might be using.
When it comes to Effect however, they enforce strict version compliance when executing Effects. An effect created by one version of Effect, must be executed by the same version of Effect:
Note
Effect will in the future log at warning level instead of crashing the program Effect-TS/effect#5064. This reproducer also has this patch applied to not crash the app prematurely
To prevent this, we added
pnpm.overridesto force a single version of Effect in the t3chat repo:Despite this, module caching led the "old version" included by Uploadthing (3.14.21) to be kept around and included in the deployed output causing the app to crash during chat generations.
I was willing to call it a day here, and conclude the issue being cache related. I had several proof of this being the case for t3chat, both locally by running
rm -rf node_modules .nextafter changing branches as well as remotely when redeploying an erroneous deployment without build cache. But this is not the end of it, and the Effect folks went ahead and dug deeper.The issue at hand
This repo tries to reproduce the issue. While I was not able to reproduce the bad cache behaviors causing the issue with
pnpm.overridesenabled, I was able to reproduce the core cause of the issue. If we look at the error message from the original tweet by Theo:This indicates that some bits of UploadThing code executed Effects passed from the application layer. This however, is not the case. No piece of Effect code from the app layer is ever touching the uploadthing layer, so how are Effect's versioned 3.16.5 being executed by Effect 3.14.21?
The reproducer
tl;dr - run
pnpm previewIt appears the version of Effect being picked up is non-deterministic. If we look closer to the Next.js compiler (thanks @fubhy for helping out debugging this bit) we can see that they are doing some form of barrel import optimizations by creating aliases for certain modules ( https://github.com/vercel/next.js/blob/canary/packages/next/src/build/create-compiler-aliases.ts#L163-L165), in particular Effect (https://github.com/vercel/next.js/blob/1f47f8f4809dd43b3223e2fe9d13f7aa1cd47caf/packages/next/src/server/config.ts#L1034-L1053). An example of such generated alias may look like this:
pnpm puts transitive dependencies inside a nested
node_modules/.pnpmfolder:But what next isn't taking into account here is that we have 2 versions. As soon as the compiler first generates this alias, all future references will use this aliased path no matter where the source is. So the uploadthing library will sometimes get v3.14.5 and sometimes v3.16.8. With normal module resolution algorithms, the uploadthing code should always resolve the
3.14entrypoints, and the application code should always resolve the3.16entrypoints.#3 applies a patch to remove this alias generation and voila... the mismatched version log goes away!