Very bumpy upgrade to 3.4.1 and Rspack - Still can't build

We’re upgrading our Meteor 3.3.2 app to 3.4.1 with Rspack. The journey has been a bit bumpy and we’re currently crashing in production (along with a few other weird things). It’s been quite a bit more work from the historical Meteor upgrade experience. I wanted to outline our issues and get support for our production build problems. I’m wondering if any of our below work-arounds is breaking the app when it actually builds in production.

While Rspack is much faster, has a smaller build, and refreshes faster, it is so much pickier than the traditional Meteor/Babel bundler. We’ve had to make the following changes:

CSS Issues

We keep a few different app-wide CSS files in ui/stylesheets. These were no longer automatically loaded. They now have to be statically imported at the end of startup/client/index.js to preserve CSS ordering alongside per-component React CSS files that live inside ui/react that do get manually imported.

Paths to images and fonts within our CSS files broke. We had to add the following to rspack.config.js:

module: {
  parser: {
    // Don't resolve url() references in CSS — pass them through as-is so the browser
    // resolves them at runtime. Old Meteor's CSS processor did the same; fonts and other
    // assets are served as Meteor public/ static files at their natural paths.
    css: { url: false },
    'css/auto': { url: false },
    'css/module': { url: false },
  },
  },

Symlink Handing

This is a big one. This is where the old Meteor bundler with Babel was very forgiving.

Our production app is broken into multiple micro-services that share a lot of code, mostly collection/schema/API code, but some client code/templates (e.g. image uploaders, etc.) as well. By default with Rspack, any symlink file’s import paths are resolved where the symlink file lives, not where it’s loaded. Meteor/Babel used the standard handling, which is the symlink file is treated exactly as if it were really there, so its relative paths load from where the symlink is located, not where the original file is located. This broke all over for us as our current codebase is architected around the standard symlink handling. We had to use the work-around from this bug report which adds the following to rspack.config.js:

module.exports = defineConfig(Meteor => {
  const swcOptions = JSON.parse(JSON.stringify(Meteor.swcConfigOptions || {}));
  if (swcOptions.jsc) {
    delete swcOptions.jsc.baseUrl;
    delete swcOptions.jsc.paths;
  }

  return {
    ...Meteor.replaceSwcConfig(swcOptions),
    resolve: {
      symlinks: false
    },
    module: {...}
   };
});

I’m not totally sure what this does, but it does make the symlinks work in the standard way. But I’m wondering if this could be what is causing us issues in our production build (see below).

HTML File Duplication

The above symlink work-around however doesn’t work on HTML Blaze files so it doesn’t resolve relative imports in symlink client files that import HTML files. Unfortunately we had to actually duplicate a few utilities and Blaze templates instead of being able to simply symlink. We’ll definitely be tracking that, as we’ve already had to make a few changes in the duplicated files. :face_with_diagonal_mouth:

Dynamic Imports

We got spoiled on the Meteor/Babel bundler dynamic import pattern:

if(Meteor.settings.public.SERVICE_ID === "ABC") {
  //  Selectively dynamically import if this is service ABC
  import('../../widget/server/utils.js').then((_ServerUtils) => {ServerUtils = _ServerUtils;});
}

//  Lower in code
if(Meteor.settings.public.SERVICE_ID === "ABC") {
  const widget = await ServerUtils._getCachedWidget(...);
  ...
}

This was nice because it lets us symlink the above file on different services, and the above pattern would dynamically load and run on the right service. Not in Rspack. It very much dislikes the above. I tried quite a few work-arounds with require and /* webpackIgnore: true */ but it will always try to resolve ../../widget/server/utils.js everywhere, which will cause an error on the services that are not "ABC" because they don’t have the file. The only solution without refactoring was suppressing the errors in rspack.config.js using the full path:

plugins: [
  new IgnorePlugin({ resourceRegExp: /^\.\/utils\.js$/, contextRegExp: /api\/widget\/server/ }),
  //  Additional IgnorePlugin entries
]

This successfully suppresses the errors when building locally and also when building in Docker. We only have a handful of these, and yes, the files should be re-architected to prevent this. But, it’s a historical pattern we’ve had that always worked in Meteor/Babel.

Symlink Import Warnings

Similar to the above Dynamic Imports, many shared symlink api files (e.g. api/widgets/server/utils.js) will have imports to functions from files that exist in all services, but are unique per service (e.g. startup/server/configurations.js). That file on service "ABC" may not export {s3Client} because that service doesn’t use AWS S3, but the api file has an import for it, but the function that uses it never runs on service "ABC". In Rspack, it throws a warning. In Meteor/Babel it allowed it. Again, this should probably be shored up with better file splitting, but it’s a pattern we used often. So rspack.config.js needed warning suppressions added:

ignoreWarnings: [
  /export 's3Client'.*was not found/,  
  //  Additional warning entries
]

I didn’t want to blanket suppress all warnings, so we added specific entries for each so we can see new ones if they occur.

Package Import Tweaks

We had a few package imports that needed their import syntax tweaked. And we had to add some aliases for a few packages’ CSS files.

Building Issues

After all of the above, we have an error- and warning-free build locally in development and our app runs totally fine. However, I can’t seem to get a Docker build working.

If I build using meteor build --directory /tmp/meteor-test-build --server-only the app successfully builds using Rspack just like it does when running the development version.

However, I have a local Docker build setup using disney:meteor-base that we use to test CI Docker builds. It builds for linux/amd64 on Apple Silicon and worked great with Meteor 3.3.2. I’ve updated to Meteor 3.4.1 (and according to the issues resolved disney:meteor-base works with Meteor 3.4.1 and Rspack), but no matter what I try, when the build gets to RUN bash $SCRIPTS_FOLDER/build-meteor-bundle.sh which just meteor build --directory $APP_BUNDLE_FOLDER --server-only, it hangs on that step and tries to run a Rspack development server with the logs:

#   "testClient": "_build/test/client-meteor.js",                                                                                                                                                
#   "testServer": "_build/test/server-meteor.js"                                                                                                                                                 
# }                                                                                                                                                                                              
# [i] Rspack DevServer Port: 8080                                                                                                                                                                
# [i] Rspack default config: /opt/src/node_modules/@meteorjs/rspack/rspack.config.js                                                                                                             
# [i] Rspack custom config: /opt/src/rspack.config.js 

Clause says its mistakenly thinking its in the development environment. I’ve thrown a bunch of Claude suggestions at it and nothing is working.

For production, we deploy to AWS ECS using Bitbucket Docker Pipeline and build on linux/amd64. The build process was successful in a test for staging, but the actual deployed builds crashed out hard with the browser console filling up with dozens of errors:

Uncaught ReferenceError: Package is not defined
Uncaught ReferenceError: Package is not defined
Uncaught ReferenceError: Package is not defined

So weren’t currently unable to get a local Docker build test working or get a working build for our staging CI workflow.

And we’re only testing one micro-service to begin with and it actually doesn’t use any symlinks and has nothing too weird in the rspack.config.js, so it’s nothing related to that, so far as I can tell.

It’s good to see more projects migrating to the Meteor-Rspack integration. We received a lot of feedback during the bundler rehaul beta period, which lasted many months of focused development, and we still continue to receive reports that we would like to address.

At the same time, we have been trying to dedicate time to other areas. Although we already started focusing on the CapacitorJS integration with initial good news (it compiles! :grinning:), it may be necessary to postpone it to Meteor 3.7 in favor of improving support around the recently delivered Rspack integration and addressing the issues, extensions, and reports coming from the community.

We are currently researching and fixing Rspack-related reports involving Blaze, test environment issues with TLA and Mocha, and other issues tracked under the modern build stack label on GitHub. We also have an ongoing PR for default Rspack 2.0 support, which is already working successfully and is planned for Meteor 3.6, along with other new minor features in other areas.

So the timing should work well for us to address these reports. If possible, we would ask for minimal reproductions of any issues found in production environments so we can isolate those edge cases, fix them, and track them properly through GitHub issues; as we have been doing so far.

Going through each of the issues you reported in this post:

CSS issues

We keep a few different app-wide CSS files in ui/stylesheets. These were no longer automatically loaded.

This is indeed true. Any code outside the entry folder is automatically ignored by Meteor and delegated exclusively to Rspack. Rspack does not have a mechanism to autodiscover and compile CSS files the same way Meteor did through Atmosphere CSS build plugins and its plain CSS procedures.

As mentioned in the build plugins requirements section of the docs, you can make any CSS file be compiled by Meteor again by using the new "meteor.modules" field in package.json. There, you can add the paths to the files you want Meteor to process, and they will behave as they did before Rspack.

{
  "meteor": {
    "modules": ["styles/main.css"]
  }
}

Then, ensure those files are not explicitly imported anywhere. They will do globally using Meteor as before.

If you want those files to be part of Rspack and benefit from its optimized build pipeline and HMR specifics, then yes, they should be imported explicitly.

About the URL trick you used, we already provided a fix for that based on a report during the Meteor-Rspack integration beta period:

I’m not sure why you needed to add those entries to the Rspack config parser, but maybe it is an edge case we could improve. We could probably handle it in the custom Meteor-Rspack plugin we created, AssetExternalsPlugin, so URLs are properly externalized by default.

Any way to reproduce the problem in a shared repository app using Meteor 3.4.1?

Symlink handling

Issues with symlink handling have indeed been reported already. But we were waiting for SWC and Rspack fixes first to see how those changes impact Meteor-Rspack apps.

Good to see the mentioned workaround works for you. I was about to apply it to the @meteorjs/rspack package config, but jsc.paths is needed to make / root imports compatible. This is a pattern used in Meteor projects forever, and we need to set those paths to keep backward compatibility.

With new SWC’s preserveSymlinks introduced, we may need to keep jsc.paths defined while making symlinks behave properly. Or just adopt contribution fix here that feels enough: fix(rspack): preserve symlinks by dropping default SWC baseUrl by sanki92 · Pull Request #14417 · meteor/meteor · GitHub. Here, we need time to dive into the different options and ensure our existing E2E scenarios, and the new reported symlink reproductions, do not break. Now that Rspack’s swc_core seems to be bumped and enabled preserveSymlinks, we should be able to review the available options in Meteor.

HTML file duplication

Actually, in Blaze there is a Meteor-Rspack specific plugin to resolve HTML files and ensure they get properly mapped to Meteor build specifics. I’m not sure if the problems with symlinks could be similar and related to this part. But we have pending work on some Blaze reports around this resolution on complex projects, and this may be the perfect time to also check its impact on symlinks.

Could I ask you for the problematic setup in a minimal Blaze repository with symbolic links on HTML files? I am going to research this, but any time saved by a direct report will allow us to move faster, validate the scenario, and maybe move it to our E2E tests to avoid regressions in the future.

Dynamic imports

This is a specific setup with symbolic links and dynamic imports, so we would strongly appreciate a minimal repo that mimics it. We will look at it in isolation and provide the feedback needed to move forward.

If the already reported symlink issues mentioned above are solved, it may fix the other related issues as a consequence. Having the repositories ready would make it straightforward to verify that or research a complementary fix.

The same applies to Symlink import warnings.

Building issues

I can try to build an app using that image. I will let you know what I find. Again, a reproduction would be great.

I can think of one possible cause that could be problematic. What is the value of your NODE_ENV variable? Could you force it to production?

Would you be able to fork the Rspack package and use a custom version with the following code silenced?

We have already provided fixes in the past around that, but for some reason another edge case could still be triggered. If removing the code above fixes your issue when trying to build in production, I would ask for more debug details or more information about your environment. A reproduction is the key so we can properly fix it.


Anyway, we want to help with this. With your feedback and reproductions, we can cover these cases in future patches or minor Meteor versions.

Please share here all possible reproductions, or feel free to message me in our Discord channel with any useful information for the fixes.

3 Likes

This PR serves as the baseline for covering any missing edge cases in Meteor setups that still rely on app-local symlink semantics.

We have already fixed one of the reported issues, and the E2E run is green for the case where you applied a workaround. It would be good to include other cases too, if we can properly identify them with a reproduction.

1 Like

Good news, with some very deep plumbing help from Claude, we’re successfully building and running in Docker. From my very first attempt at building in Docker, there was this log near the hang:

Parcel watcher error on /opt/src: [Error: Unable to poll: Interrupted system call]

There were a lot of logs so I just ignored this, wondering if it was just an extraneous warning. After going very deep with Claude a second time, all the way into running the build line by line, checking system processes, Rspack outputs, and frigging hex code, it refocused on the above log after verifying Rspack was working correctly.

TL;DR to get the build working, I needed to add ENV METEOR_WATCH_FORCE_POLLING=true in my Docker build file to work around a possible bug. As for the bug, here is the Claude report, which I’ll happily create a bug for, unless my app config is doing something bad to cause this:


Title

meteor build hangs indefinitely in Docker due to unhandled EINTR in @parcel/watcher and missing polling fallback in safe-watcher.js


Environment

  • Meteor 3.4.1 (with rspack package)
  • Docker on Apple Silicon (reproducible with --platform linux/amd64 via QEMU and native linux/arm64)
  • tools/fs/safe-watcher.js in meteor-tool@3.4.1

What happens

meteor build hangs indefinitely, either at Loading plugin 'rspack' from package... or Building the application, consuming no CPU. The build never completes or errors — it simply waits forever.


Root cause: two compounding bugs

Bug 1 — @parcel/watcher does not retry on EINTR

@parcel/watcher uses a native Rust binary that calls poll() or epoll_wait() in a background thread to receive filesystem events from the kernel. When the Meteor build spawns child processes (npm, rspack), their exit delivers SIGCHLD to the parent, which interrupts any blocking system calls in other threads. The kernel returns EINTR (errno 4) to indicate the interruption.

The correct POSIX response is to retry the system call immediately. @parcel/watcher’s native code does not do this — it treats EINTR as a fatal error and surfaces it. This is more likely to trigger inside Docker Desktop’s Linux VM where signal delivery from the container runtime makes the poll()SIGCHLDEINTR race condition much more frequent than on bare metal.

Bug 2 — safe-watcher.js does not fall back to polling on watcher errors

When @parcel/watcher reports the EINTR error, Meteor’s safe-watcher.js logs it and removes the affected directory from watchRoots, but does not call fallbackToPolling(). It does call fallbackToPolling() for ENOSPC errors, but not for anything else:

console.error("Parcel watcher error on ".concat(osDirPath, ":"), err);
if (err.code === "ENOSPC" || err.errno === require("constants").ENOSPC) {
    fallbackToPolling(); // ← only fires for ENOSPC
}
watchRoots.delete(dirPath); // ← watch root removed, no fallback for other errors
return;

This leaves file entries and their callbacks registered in the entries map with no watcher to fire them. Meteor’s build pipeline waits for those callbacks to signal that files are ready. Since neither the native watcher nor a polling fallback is active, the callbacks never fire. The Node.js event loop blocks indefinitely on futex (confirmed via /proc/<PID>/task/<TID>/syscall).


Expected behaviour

  • @parcel/watcher should retry poll()/epoll_wait() on EINTR per standard POSIX practice
  • safe-watcher.js should call fallbackToPolling() for any watcher error, not only ENOSPC

Workaround

Set METEOR_WATCH_FORCE_POLLING=true in the build environment. This env var is already wired into safe-watcher.js and bypasses @parcel/watcher entirely in favour of fs.watchFile polling:

// safe-watcher.js
let watcherEnabled = !JSON.parse(process.env.METEOR_WATCH_FORCE_POLLING || "false");

For Docker builds, add to the builder stage in your Dockerfile:

ENV METEOR_WATCH_FORCE_POLLING=true

I will happily create some test repos for other issues. Going on vacation for a week but I will get on that upon returning, unless I get a moment.

Thanks for your support @nachocodoner

2 Likes

Just my two cents but I think you guys should first make sure that rspack is working 100%. It’s been a very tough ride even after what, several months now. Yes, it’s faster. But you don’t want to know how many frustrating hours I’ve spent to fix some of the same problems that @evolross has.

Yes, I still can’t use .only or Grep in any of my projects which use rspack (and I have one project that now run 1,600+ tests. I know you looked into it and never could replicate it but it’s still a problem that even many hours/tokens with Claude Code couldn’t solve. So I have given up but it’s a big inconvenience that has let me to postpone one migration as .only runs fine as long as I’m NOT USING rspack. So that’s the clear difference for me.

Last night I discovered why I had crashes in my IDE with OOM (Meteor 3.4 dev: build tool leaks rspack server bundle + source-map SourceNodes on every rebuild → OOM · Issue #14443 · meteor/meteor · GitHub) which was simple by constantly editing a file (eg linting it) and rspack rebuilds after every change. Great speed yes, but not managing the memory properly is a huge error that quite frankly I wasn’t expecting from you guys.

So yeah, several apps in Production which use rspack (luckily the OOM error is DEV only, as is the test problem) and it’s finally stable after months of doctoring with various problems which Ross mentions here.

So please, make sure the features like rspack are working 100% first before hopping onto new projects. Same for the bug fix I’m waiting for which was approved by you and then @italojs nixed it again. I have a workaround in my code but I’d rather see you fix bugs then work on new things.

There are a lot of people here that rely on MeteorJS for Production services, not just for fun projects. Stability and bug fixing in due time is very important. Maintenance is very important. No one is making a big fuzz about problems that we had for many, many years (like CapacitorJS) or improvements like Streams taking a bit longer.

Just my two cents and not meant as a personal critique of yours or any of the other stuff at Tiny.

1 Like

No worries, I don’t take it personally. Also, I don’t think it’s necessary to mention that disagreeing with something is personal. We can discuss it. Opinions are welcome.

I partially agree with this. That’s why I mentioned that we are partially shifting our focus back to Rspack issues. We have been doing this since its initial beta about a year ago.

I don’t fully agree that we should give it 100% of our focus, though, especially since the number of Rspack reports dropped a lot months ago after the beta period. But I would say our focus can now be at least 50% as we validate reproducible issues and features ahead, like Rspack 2.0, that users would like to have.

Change Streams have had their own history of being pushed forward. Also, with OPLOG being considered by MongoDB as an internal API they may cut, moving away from it has been a requirement for a long time. The feedback on 3.5 has been really positive, with runtime performance contributions and extra development from many community members. So the fuzz and disccusion is not only about complaints, it’s also about excitement around positive efforts.

CapacitorJS has similarly been requested for years. You can look for it in forum posts. It was asked for continuously, even when the modern build stack was the main focus. Besides that, it’s known that CordovaJS being unmaintained is going to become a problem really soon. For example, some plugins like RevenueCat’s Cordova plugin will drop support in August 31th 2026, really close date. This is a plugin used beyond fun projects and critically needed in production apps. This is just one example of how we should keep a wide focus and apply our efforts across areas.

There has also been a lot of private and direct communication over the years asking for these things. With this, I want to express how important it is to give weight to each area we want to improve, and to have a healthy space and time to work on them.

So I agree that Rspack needs attention, and it is getting a large percentage of our time these days to prepare the fix and deliver them on next patches or feature releases. That will continue because of the latest reports and the excitement around Rspack 2.0. At the same time, other areas of Meteor also deserve thoughtful and transversal time and focus, considering other angles from users who need them.

Thanks for this report. We have had issues with memory. Many of them are solved on our side on meteor 3.4.1, while others rely on Rspack itself. I expect these issues to happen in tools, as there are still humans behind the development, not fully AI agents.

The good thing is that the people on the Rspack side are also aware of the memory issues they have, especially on large projects and with persistent cache enabled, which Meteor uses. Rspack 2.0 is about to deliver those improvements, so our focus moved to prepare Rspack 2.0 which is ready for next Meteor 3.6 release. I’m not sure yet if that will be the case for you. I will check whether disabling the persistent cache in your reproduction helps, so we can discard whether it is a Meteor-side problem.

I will also research your report more deeply in the following days. Keep them coming, since our efforts and focus will continue on improving the Rspack experience.

4 Likes

Thanks for the detailed description of your build issues. Good that you caught it.

It is weird that this issue did not happen on Meteor 3.3.2, since the modern watcher was introduced in Meteor 3.3. Not sure how Rspack is related with it, but definitely it has to do with the modern build stack improvements. I think we also fixed an issue in this area later in Meteor 3.4/3.4.1, and it may have caused another regression in some environments. I will take your notes and see if there is anything else we can do to fix those two compounding bugs you reported, with proper E2E regressions tests to avoid them in future.

Anytime. With reproductions, we should be able to address them more easily. Enjoy your rest time!

3 Likes

Hey @a4xrbj1, Your concerns are valid and I can relate to the frustration when something you rely on in production isn’t as solid as it should be.

I also would like to know which PR I nixed, tried to find some PR with our name or find it via this thread context but did’nt find it. But if a fix was declined or asked to be revised during review, it was for technical reasons, not to block progress, I’m happy to review it again and give a better priority on it.

More broadly about what @nachocodoner described, it reflects the actual reality of maintaining an open source framework with a small team. “100% stable before anything new” sounds reasonable in principle, but in practice it can mean never shipping CapacitorJS while Cordova plugins drop support in August, or never delivering Change Streams while MongoDB deprecates oplog access. These aren’t vanity features, they’re responses to real production pressure from other parts of the community.

That said, I hear you. The .only/grep regression and the OOM issue are real problems that affect developer experience daily. The fact that you’ve worked around them doesn’t mean they should stay open. We’re not hopping from shiny thing to shiny thing. The Rspack work is ongoing, as this thread shows with nachocodoner’s PRs opened 2 days ago. That’s the reality on the ground.

Thanks for the feedback, following it I can say we should give more attention in the recent releases issues, advice noted and i promise we will put more effort on this kind of situation next releases.

3 Likes

We’re building successfully, but are now stopped cold when deploying to staging with the browser console error:

Uncaught Error: Cannot find module '@swc/helpers/_/_tagged_template_literal'

As reported in bug 14397, except we’re seeing it across the board in all browsers/agents/builds.

I also created a bug report for my above polling issue. I’ll try to get a small repo/setup created that reproduces it.

Amazing, it will help us to nail it for sure.

btw, I hope to bring those fixes into 3.5.1