Vitest question
Why are my .env values undefined in Vitest, locally or in GitHub Actions?
Vitest loads only variables that start with VITE_ from .env files, so DATABASE_URL, SUPABASE_SERVICE_ROLE_KEY and NEXT_PUBLIC_ names are undefined in a test unless you load them. The Vitest docs show loadEnv(mode, process.cwd(), '') from vite assigned to test.env. A second cause shows up in GitHub Actions: an .env file that is not committed is not on the runner, so even VITE_ names are undefined there. Set them in the workflow with env, or in the Vercel project when the build command runs the tests. Reproduced with vitest 5.0.5 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 variables the code reads that .env.example does not list
For AI-built Vercel + Supabase apps, run the command below from your app folder with Node.js 20 or newer. When the code reads a name such as import.meta.env.VITE_API_URL or process.env.PAYMENTS_API_KEY and .env.example does not list it, the check reports env_var_drift as a warning that names each variable, and it cannot see the variables set in your Vercel project or in your GitHub Actions settings. It does not run Vitest or read vitest.config, so it cannot tell whether a test loads the name.
It is a repository check, not a live database test: it does not start your app or call any provider. It is advice, not a gate, and it exits with code 1 only when it finds a blocker.
npx -y viberaven@1.7.1 checkWhat it printed on a fixture repo
On 2026-10-31 we ran it on a small Vite + React fixture repo made for this page, not a customer repo, with vitest in devDependencies, a src/api.ts that read import.meta.env.VITE_API_URL and process.env.PAYMENTS_API_KEY, and an .env.example that listed neither. The output is below, with the folder shortened to ~/notes-app. The finding is a warning, so 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 (PAYMENTS_API_KEY, VITE_API_URL). 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.mdWhich variables does Vitest load from .env files?
The Vitest docs say it autoloads only variables prefixed with VITE_ from .env files, the same convention Vite uses. In a run on 2026-10-31 with .env, .env.local, .env.test and .env.test.local in the project root, a VITE_ name from each file was visible in both import.meta.env and process.env, and the mode printed as test. Names without the prefix were undefined in both. Vite's docs say mode-specific files win over the generic ones, and that a variable already set in the shell is not overwritten by a file.
VITE_SUPABASE_URL https://example.supabase.co (.env)
VITE_FROM_LOCAL local (.env.local)
VITE_MODE_LOCAL testlocal (.env.test.local)
NEXT_PUBLIC_SUPABASE_URL undefined
SUPABASE_SERVICE_ROLE_KEY undefinedHow do I load every variable from .env in Vitest?
Use the method the Vitest docs name: loadEnv from vite, with an empty prefix, assigned to test.env. The Vitest config docs say test.env sets variables on process.env and import.meta.env during tests, and that they are not available in the main process, for example in globalSetup. With this config, NEXT_PUBLIC_SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY were defined in both places in the same run. It does not change what a browser bundle gets, which follows envPrefix (VITE_ unless you change it). Put test values in the file, not a production service role key.
import { defineConfig } from "vitest/config"
import { loadEnv } from "vite"
export default defineConfig(({ mode }) => ({
test: {
env: loadEnv(mode, process.cwd(), ""),
},
}))Why does CI fail with "supabaseUrl is required." when the test passes on my machine?
A client created at import time with createClient(import.meta.env.VITE_SUPABASE_URL, ...) gets undefined when no .env file exists. In a run with supabase-js 2.118.0 and no .env, the test failed with the message below, thrown from validateSupabaseUrl in the library's helpers.ts; with a .env that held a fake URL it passed. On a runner, set the names yourself. GitHub's docs set variables with env at the workflow, job or step level and pass a secret as env: NAME: ${{ secrets.NAME }}. Secrets are not passed to a workflow triggered from a forked repository, which is the case in secrets empty in fork pull requests.
Error: supabaseUrl is required.
❯ validateSupabaseUrl ../node_modules/@supabase/supabase-js/src/lib/helpers.ts:150:11
❯ new SupabaseClient ../node_modules/@supabase/supabase-js/src/SupabaseClient.ts:321:21
❯ createClient ../node_modules/@supabase/supabase-js/src/index.ts:66:10
# .github/workflows/test.yml (the env part)
jobs:
test:
runs-on: ubuntu-latest
env:
VITE_SUPABASE_URL: ${{ vars.VITE_SUPABASE_URL }}
VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }}
steps:
- run: npx vitest runChecklist
How to fix it, then check the fix
- 01Find variables the code reads that .env.example does not list. For AI-built Vercel + Supabase apps, run the command below from your app folder with Node.js 20 or newer. When the code reads a name such as import.meta.env.VITE_API_URL or process.env.PAYMENTS_API_KEY and .env.example does not list it, the check reports env_var_drift as a warning that names each variable, and it cannot see the variables set in your Vercel project or in your GitHub Actions settings. It does not run Vitest or read vitest.config, so it cannot tell whether a test loads the name. It is a repository check, not a live database test: it does not start your app or call any provider. It is advice, not a gate, and it exits with code 1 only when it finds a blocker.
npx -y viberaven@1.7.1 check - 02Print what the test really sees. Vitest 5.0.5 showed console output of a passing test only with the verbose reporter in our run:
// in the test console.log(import.meta.env.MODE, process.env.DATABASE_URL) # then run npx vitest run --reporter=verbose - 03If the name has no VITE_ prefix, load it with loadEnv in vitest.config.ts as above, or list it under test.env. Use test values only.
- 04Add the name to .env.example, so a fresh clone and a runner both know the test needs it.
- 05In GitHub Actions, set each name with env, using vars for plain values and secrets for keys. Do not rely on a local .env file there.
- 06If your Vercel build command runs vitest before the build, add the same names to the Vercel environment that build uses. Vercel applies changed variables only to new deployments, so redeploy.
- 07Run vitest again in the workflow and confirm that the log shows no "is required" or "undefined" error.
Related
Related guides
Sources
Official docs behind this checklist
- Vitest, Features (environment variables, loadEnv)
- Vitest, test.env
- Vite, Env variables and modes
- Vite, JavaScript API (loadEnv)
- Vite, Shared options (envPrefix)
- GitHub Actions, Store information in variables
- GitHub Actions, Using secrets
- Vercel, Environment variables
- supabase-js, lib/helpers.ts
- Vitest issue 3735, functions with parameters cannot find import.meta.env
Browse all launch guides · Read resources · How to make and check your next change
FAQ
FAQ for builders before launch
Does .env.local load in Vitest?
Yes in our run: a VITE_ name in .env.local and one in .env.test.local were both visible. Vite's docs list .env and .env.local as loaded in all cases and .env.[mode] files only in that mode. Vitest runs in mode test.
Does loadEnv put my secrets in the browser bundle?
Not by itself. test.env sets values for the test processes. What a browser bundle receives is decided by envPrefix, which is VITE_ unless you change it, so keep real secrets out of names with that prefix.
Why is import.meta.env undefined inside a helper that takes arguments?
Vitest issue 3735 reports that Vitest replaces import.meta.env with process.env, so a function with its own parameter named process breaks. This is a report from an issue thread, not from the docs.