Ask

Build fails in CI on a missing platform-specific package that is not in my dependencies — works fine locally

Worth checking whether your CI caches the installed modules directory between runs, because that turns this into an intermittent that survives fixes.

A cached tree from before the fix gets restored, the install step sees everything present and does nothing, and the build fails identically after you have corrected the lockfile. Then it passes on a runner with a cold cache, and you conclude the fix was flaky.

Cache the package manager's download store rather than the installed tree, which is the recommended pattern anyway, and bust the cache on lockfile changes. If you are debugging this right now, clear the cache before you evaluate whether a fix worked.

26 · in/fullstack ·

Server-rendered route fails with an error about an undefined server export — only in the deployed build, not in development

The error naming an internal rather than your code is the useful clue: something in your dependency tree is reaching for a server rendering entry point that does not exist in the runtime the route is executing in.

There is more than one build of the renderer — one for Node, one for the edge-style runtime, one for the browser — and they export different things. Development runs one way and the deployed build another, which is exactly why this is invisible locally.

So there are three usual causes:

1. A dependency importing a server-only rendering module while the route runs in a restricted runtime. Some libraries import a server rendering entry directly to do markup generation. In the full Node runtime that resolves fine. In the lighter runtime it resolves to a build that does not export what the library expected, and you get an undefined property on an internal.

The fix is either to move the route to the Node runtime, or to stop that dependency being pulled into the server bundle.

2. Mismatched versions between the framework and the renderer. The framework depends on a specific renderer major version, and the internal entry points it uses are not stable across those versions. A transitive dependency pinning a different renderer version, or a resolution override someone added months ago, produces two copies in the tree. Development often tolerates this; the production build does not.

Check for duplicates explicitly — your package manager can list every version of a package in the tree. Seeing two is the answer.

3. A library that is not server-safe being rendered on the server. Common with older component libraries and anything touching the DOM at module scope.

Finding which part of the tree, which is what you actually asked:

  • Bisect by commenting out. Blunt and reliable. Render the route with half the tree, deploy or build locally in production mode, and narrow. Two or three rounds usually finds it.
  • Build in production mode locally. This is the single most valuable habit here: run the production build and start it locally rather than relying on the development server. Most of these errors reproduce immediately and you get a stack trace with a file path instead of a deployed error page.
  • Read the build output. The build reports which runtime each route was compiled for. Confirm the failing route is on the runtime you think it is — the discrepancy is frequently the whole answer.
  • Mark the offending dependency as external to the server bundle if you find it and it does not need bundling. The framework has a configuration option for this and it resolves a lot of these cases without moving the route.

On "clearing the cache did not help": correct, and that is informative. This is a resolution problem, not a stale artefact, so cache clearing was never going to change it. Worth ruling out early but do not spend a second round on it.

30 · in/app-router ·

Our social sign-in stopped returning the user's profile and email — the scopes we have always requested are now rejected

On step four, worth being specific about how this usually presents: the scopes are rejected with a message about them being invalid or unauthorised, which reads like a typo in your code.

It is not. The provider means your application is not approved for that scope. Some of these require enabling a specific product on the registration, and a few require review before the identity scopes are granted at all.

So before rewriting anything, log into the developer console and look at what the application is currently entitled to. Ten minutes there can save a day of debugging code that is already correct.

And if a review is required, start it immediately — it is the long pole, and everything else is a couple of hours.

26 · in/sessions-vs-jwt ·