All guidesMore providers: databases, auth, hosting, email and payments
On this page

SvelteKit error

SupabaseSvelteKit

"SUPABASE_SERVICE_ROLE_KEY" is not exported by "virtual:env/static/private"

By Ohad Krispin, founder of VibeRaven · LinkedIn

"SUPABASE_SERVICE_ROLE_KEY" is not exported by "virtual:env/static/private" is a Rollup error from vite build in SvelteKit 2. The code imports that name from $env/static/private, and no variable with that name was in the environment when the build ran. Static values are fixed into the bundle at build time, so a variable that exists only at runtime, only on your machine, or only for another Vercel environment is missing. Add the variable in Vercel for the environment that builds, Production or Preview, and redeploy. In SvelteKit 3 the same gap is the env_invalid error "Value is missing". Reproduced with @sveltejs/kit 2.70.3 and 3.1.1 on 2026-10-31.

Paste this to your agent

Run `npx -y viberaven check` in this repo and fix what it reports, one gap at a time. Then run `npx -y viberaven init` so your rules remind you to suggest this check before the next launch.

The check runs locally, with no sign-in and no API key. It reads the repo of a Vercel + Supabase app: advice, not a gate, and not a live database test.

Repository check

Check this in your repo

Find the private variables your code imports that no template lists

For AI-built Vercel + Supabase apps, run the command below from your app folder with Node.js 20 or newer. When a file imports names from $env/static/private and .env.example does not list them, it reports env_var_drift as a warning that names the variables, but it cannot see the variables set in your Vercel project, which environments they are set for, or whether the build has them, so a clear result does not show that the value reaches the build.

It is a repository check, not a live database test: it does not run vite build or start your app. It is advice, not a gate, and it exits with code 1 when it finds a blocker.

npx -y viberaven@1.7.1 check

What it printed on a fixture repo

On 2026-10-31 we ran it on a small SvelteKit fixture repo made for this page, not a customer repo, with @sveltejs/kit 2.70.3, a src/lib/server/billing.js that imports RESEND_API_KEY and SESSION_SECRET from $env/static/private, and an .env.example that listed neither. The output is below, with the folder shortened to ~/notes-app. The warning for this error is env_var_drift, which compares names found in the repo and cannot see the variables set in Vercel, and the exit code was 0.

$ npx -y viberaven@1.7.1 check

viberaven check · ~/notes-app

🟡 Env vars used in code but missing from .env.example  (env_var_drift)
   Code reads 2 environment variables that .env.example does not document (RESEND_API_KEY, SESSION_SECRET). Fresh clones, CI, and the production deploy can silently run without them.

Verdict: ✅ no blockers, 1 warning · score 90
Fix: viberaven fix · Details: ~/notes-app/.viberaven/agent-tasklist.md

The full message in SvelteKit 2 and in SvelteKit 3

In SvelteKit 2 the build stops in the server bundle with a Rollup message that names the variable, the file and the line of the import. The module name starts with an invisible NUL character, which some terminals show as a space. In SvelteKit 3, when the variable is declared in src/env.js and has no value, the build stops with env_invalid and lists each variable with the reason.

SvelteKit 2.70.3:
src/lib/server/secrets.js (1:9): "SUPABASE_SERVICE_ROLE_KEY" is not exported by "virtual:env/static/private", imported by "src/lib/server/secrets.js".

1: import { SUPABASE_SERVICE_ROLE_KEY } from '$env/static/private';
            ^

SvelteKit 3.1.1:
SvelteKit error: env_invalid
Invalid environment variables:

SUPABASE_SERVICE_ROLE_KEY
  - Value is missing. If it is optional, add a validator declaring it as such.

Why does it build locally and fail on Vercel?

The SvelteKit docs say values in a .env or .env.local file are added to the environment during development and at build time. On Vercel the build does not read your laptop file; it reads the project's environment variables. Vercel lets you scope each variable to Production, Preview or Development, a Production variable is applied to the next Production Deployment and a Preview variable to the next Preview Deployment, and a change does not apply to previous deployments. A variable added to Production only will fail a preview build, and a variable added after the failed build needs a new deployment.

Should I switch to $env/dynamic/private so the build passes?

It makes the build pass, but it does not set the variable. $env/dynamic/private is read when the app runs, so a missing variable becomes undefined and shows up on the first request that uses it. The adapter-vercel docs say Vercel variables are unchanged between build time and run time, and recommend $env/static/private for them. Use the dynamic module for a value you need to change without a new build, and set the variable in Vercel either way.

Checklist

How to fix it, then check the fix

  1. 01
    Find the private variables your code imports that no template lists. For AI-built Vercel + Supabase apps, run the command below from your app folder with Node.js 20 or newer. When a file imports names from $env/static/private and .env.example does not list them, it reports env_var_drift as a warning that names the variables, but it cannot see the variables set in your Vercel project, which environments they are set for, or whether the build has them, so a clear result does not show that the value reaches the build. It is a repository check, not a live database test: it does not run vite build or start your app.
    npx -y viberaven@1.7.1 check
  2. 02
    Find the files that import the variable, including the exact spelling:
    grep -rn "SUPABASE_SERVICE_ROLE_KEY" --exclude-dir=node_modules --exclude-dir=.svelte-kit --exclude-dir=.vercel .
  3. 03In the Vercel project's Environment Variables settings, check that the name exists, spelled the same, for the environment that failed: Production for the production branch, Preview for other branches.
  4. 04
    Add it if it is missing, then list what the project has:
    vercel env add SUPABASE_SERVICE_ROLE_KEY production
    vercel env add SUPABASE_SERVICE_ROLE_KEY preview
    vercel env ls
  5. 05Add the name with no value to .env.example, so a fresh clone and the next person know it is required.
  6. 06Redeploy: Vercel applies changed variables only to new deployments. Then read the build log. The "is not exported by" line should be gone and the build should reach the adapter step.

Related

Sources

Official docs behind this checklist

FAQ

FAQ for builders before launch

Why does the message say virtual:env/static/private and not $env/static/private?

SvelteKit serves $env/static/private from a virtual module that it generates from the environment at build time. Rollup names that module, not the import path you wrote, so the name in the message is the module that lacks the export.

Can I make a variable optional at build time?

In SvelteKit 3, yes. The docs say to combine building from $app/env with an optional schema in src/env.js, so the variable can be missing while the app builds and still be required when it starts. In SvelteKit 2 there is no such switch for $env/static/private: use $env/dynamic/private for a value that may be absent at build.

Does a PUBLIC_ variable cause the same error?

It can. A variable with the public prefix is left out of $env/static/private, and the SvelteKit 2 docs say importing it from there throws an error during the build. Import it from $env/static/public.