withastro/astro · error · AstroError

EnvInvalidVariables

EnvInvalidVariables

Error message

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

${errors}

What it means

At dev/build time Astro validates every variable declared in `env.schema` against the actual environment. When one or more fail (missing value, or a value that does not satisfy the declared type) and this is not a type-generation-only sync run, it throws EnvInvalidVariables listing each invalid key, its declared type, and the specific validation errors.

Solutions

  1. Set every listed variable where Astro reads it: .env (and .env.production) for local, deployment secrets/environment variables for CI and production.
  2. If absence is a supported state, declare the variable optional (envField.string({ access: 'secret', optional: true })) and handle undefined in code.
  3. Fix declared types to match reality - correct enum values, number where the value is numeric.
  4. Add an env preflight step to CI that fails before the build when required variables are missing.

Example fix

// before - required secret that is never set anywhere
export default defineConfig({
  env: { schema: { STRIPE_KEY: envField.string({ access: 'secret' }) } },
});

// after - optional with an explicit server-side check
export default defineConfig({
  env: { schema: { STRIPE_KEY: envField.string({ access: 'secret', optional: true }) } },
});
Defensive patterns

Strategy: validation

Validate before calling

// preflight before `astro build` in CI
const required = ['SECRET_API_KEY', 'PUBLIC_SITE_URL'];
const missing = required.filter((k) => !process.env[k]);
if (missing.length) {
  console.error('missing env vars:', missing.join(', '));
  process.exit(1);
}

Prevention

When it happens

Trigger: Declaring `STRIPE_KEY: envField.string({ access: 'secret' })` but never providing STRIPE_KEY; declaring a number or enum while the environment holds a non-matching string ('abc' for number, a value outside the enum); a required public variable missing at build time in CI; optional/required mismatches between schema and reality.

Common situations: A teammate cloning the repo without the project's .env file; CI or deployment platform missing configured secrets; renaming a variable in env.schema but not in the deployment environment; values quoted or formatted so the declared type rejects them.

Related errors


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

Appendix: 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 52e6c34790)