Astro upgrade dependency conflicts
Astro upgrade dependency conflicts

Upgrading to Astro 6, Svelte 5, and Better Auth 1.6: What Broke and How I Fixed It

I spent a Sunday upgrading giorgiosaud.io from Astro 5 to Astro 6, @astrojs/svelte 7 to 8, and better-auth 1.4 to 1.6. The build broke in three distinct ways. Here’s a post-mortem so you don’t have to rediscover each one.

Issue 1: Async IIFE inside onMount breaks Svelte TypeScript preprocessing

After upgrading @astrojs/svelte to 8.x, the build started failing with:

[vite-plugin-svelte:compile] Unexpected token

The error pointed to a type annotation like : boolean that hadn’t been stripped from the compiled output — raw TypeScript reaching the Svelte compiler.

The cause was a semicolon-prefixed async IIFE inside onMount:

<script lang="ts">
onMount(() => {
  ;(async () => {
    pushSupported = isPushSupported()
    // ...
  })()
})
</script>

When vitePreprocess({ script: true }) runs esbuild to strip TypeScript from Svelte <script lang="ts"> blocks, the (async token creates an ambiguity in TypeScript mode. It can look like the start of a generic call expression (async<Type>(...)), which confuses esbuild’s parser boundary. The result is that type annotations later in the same script block don’t get stripped.

Fix: replace the IIFE with a named async function.

<script lang="ts">
onMount(() => {
  async function initPush() {
    pushSupported = isPushSupported()
    // ...
  }
  initPush()
})
</script>

Issue 2: TypeScript as casts in Svelte templates

Another Svelte compile error surfaced from this pattern in a template:

<span class:admin={(user as { role?: string }).role === 'admin'}>
  {(user as { role?: string }).role === 'admin' ? t.admin : t.user}
</span>

TypeScript as casts are not valid in Svelte template expressions — templates compile to JavaScript, not TypeScript. The preprocessor only handles the <script> block.

Fix: move the cast logic into the script block as a derived value.

<script lang="ts">
  const isAdmin = $derived((user as { role?: string })?.role === 'admin')
</script>

<span class:admin={isAdmin}>
  {isAdmin ? t.admin : t.user}
</span>

Issue 3: Transitive dependency version conflicts with better-auth 1.6

[email protected] ships with nested copies of its own peer dependencies at the versions it was built against. If older versions of those packages are hoisted to the top of node_modules, the build breaks.

I hit three conflicts:

PackageHoisted versionRequired versionError
@better-auth/core1.4.101.6.9Missing ./utils/error-codes specifier
better-call1.1.71.3.5Missing export kAPIErrorHeaderSymbol
@peculiar/asn1-schema2.6.02.7.0Cannot get schema for 'AlgorithmIdentifier'

Fix: add overrides (and resolutions for bun compatibility) to package.json:

{
  "overrides": {
    "@better-auth/core": "1.6.9",
    "better-call": "1.3.5",
    "@peculiar/asn1-schema": "2.7.0"
  },
  "resolutions": {
    "@better-auth/core": "1.6.9",
    "better-call": "1.3.5",
    "@peculiar/asn1-schema": "2.7.0"
  }
}

Issue 4: estree-walker conflict breaks Astro config loading on Vercel

Even after fixing the above, Vercel builds failed with:

[astro] Unable to load your Astro config
No "exports" main defined in /vercel/path0/node_modules/estree-walker/package.json

[email protected] is ESM-only and has a proper exports field. [email protected] does not. Something in the dependency tree was pulling in 2.0.2 as the hoisted version on Vercel’s environment.

Locally the build passed because 3.0.3 was already the hoisted version from a previous install. Vercel restored a build cache from before the upgrade, so it had 2.0.2 locked in.

Fix: add estree-walker to overrides, and crucially — clear the Vercel build cache before redeploying. Without clearing the cache, Vercel restores the old node_modules and the override never takes effect.

{
  "overrides": {
    "estree-walker": "3.0.3"
  },
  "resolutions": {
    "estree-walker": "3.0.3"
  }
}

To clear cache in Vercel: open the failed deployment → Redeploy → check “Clear cache and redeploy”.

The pattern

All four issues share the same root: a package that bundles its own dependencies at a specific version gets shadowed by an older hoisted version. The fix is always the same — force the correct version with overrides/resolutions — but diagnosing which package is conflicting requires reading the error carefully and cross-referencing nested node_modules.

If you hit Missing specifier or does not provide an export named errors after a major dependency upgrade, check for nested node_modules with find node_modules -name "package.json" -path "*/<package>/package.json" | xargs grep '"version"' and compare versions. The nested one is what the package needs; the hoisted one is what’s breaking it.

Link copied!

Comments for pgrdng