withastro/astro · error · AstroError

EnvInvalidVariables

EnvInvalidVariables

Error message

The following environment variables defined in `env.schema` are invalid:

${errors}

What it means

During the build (non-sync mode), Astro validates every variable declared in `env.schema` against the loaded environment. Any variable whose validator returns `result.ok === false` is collected into an `invalid` list, and if that list is non-empty at the end, Astro aborts the build with `EnvInvalidVariables` listing each failing key, its declared type, and the validator errors.

Source

Thrown at packages/astro/src/env/vite-plugin-env.ts:160

	for (const [key, options] of Object.entries(schema)) {
		const variable = loadedEnv[key] === '' ? undefined : loadedEnv[key];

		if (options.access === 'secret' && !validateSecrets) {
			continue;
		}

		const result = validateEnvVariable(variable, options);
		const type = getEnvFieldType(options);
		if (!result.ok) {
			invalid.push({ key, type, errors: result.errors });
			// We don't do anything with validated secrets so we don't store them
		} else if (options.access === 'public') {
			valid.push({ key, value: result.value, type, context: options.context });
		}
	}

	if (invalid.length > 0 && !sync) {
		throw new AstroError({
			...AstroErrorData.EnvInvalidVariables,
			message: AstroErrorData.EnvInvalidVariables.message(invalidVariablesToError(invalid)),
		});
	}

	return valid;
}

let cachedServerTemplate: string | undefined;

function getTemplates({
	schema,
	validatedVariables,
	loadedEnv,
}: {
	schema: EnvSchema;
	validatedVariables: Array<ValidVariable>;
	loadedEnv: Record<string, string> | null;

View on GitHub (pinned to d081033d5f)

Solutions

  1. Read the error's per-key list and set each missing/invalid variable to a value that satisfies its declared type in the target environment.
  2. If a value is genuinely optional, give the schema entry a default or make validation tolerant per the env.schema API.
  3. Confirm `.env` is discoverable: check `envDir` and that Vite's `loadEnv` is picking up the file for the current mode.
  4. Re-run `astro build`/`astro dev` after fixing to confirm the validator passes.

Example fix

// before
export default defineConfig({
  env: { schema: { PORT: env.number() } }  // build fails if PORT unset
})

// after — supply a default so validation passes when unset
export default defineConfig({
  env: { schema: { PORT: env.number({ default: 4321 }) } } })
Defensive patterns

Strategy: validation

Validate before calling

import { loadEnv } from 'vite';
// Pre-flight: check every schema var resolves and validates before build.
function preflightEnv(schema, mode, envDir) {
  const env = loadEnv(mode, envDir, '');
  const missing = [];
  for (const [key, opt] of Object.entries(schema)) {
    if (env[key] === undefined && opt.access !== 'public') missing.push(key);
  }
  return missing;
}

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: An env variable declared in `env.schema` is missing, malformed, or fails its declared type validator at build time — e.g. `env.number('PORT')` when PORT is unset or non-numeric, or `env.enum(...)` when the value isn't one of the allowed choices. Suppressed only when `sync: true` is set.

Common situations: Forgetting to set a required env var in CI or a deploy target; a `.env` file not loaded because `loadEnv`/`envDir` is misconfigured; a type mismatch after changing a variable's schema type; a secret that's empty or whitespace in production.

Related errors


AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12). Data as JSON: /api/errors/e74e9a24e0baa4e0. Report an issue: GitHub.