withastro/astro · critical · AstroError

EnvPrefixConflictsWithSecret

EnvPrefixConflictsWithSecret

Error message

The following environment variables are declared with `access: "secret"` in `env.schema`, but their names match a prefix in `vite.envPrefix`, which would expose them in client-side bundles:

${conflicts.map((c) => `- ${c}`).join('\n')}

Either remove the conflicting prefixes from `vite.envPrefix`, or rename these variables to use a prefix not in `vite.envPrefix`.

What it means

astro:env validates your config before serving or building. This error fires when a variable declared with `access: 'secret'` in `env.schema` has a name matching one of the prefixes listed in `vite.envPrefix`. Vite exposes matching variables on `import.meta.env` in client bundles, so the collision would inline a secret into shipped JavaScript. The message lists every conflicting variable name.

Solutions

  1. Rename the secret variables so they no longer start with any vite.envPrefix prefix.
  2. Remove the conflicting prefixes from vite.envPrefix and read those values through `astro:env/server` instead of import.meta.env.
  3. If a variable genuinely must reach the browser it is not a secret - declare it `access: 'public'` in env.schema.

Example fix

// before - astro.config.mjs
export default defineConfig({
  vite: { envPrefix: ['SECRET_'] },
  env: { schema: { SECRET_API_KEY: envField.string({ access: 'secret' }) } },
});

// after
export default defineConfig({
  env: { schema: { SECRET_API_KEY: envField.string({ access: 'secret' }) } },
});
// read server-side: import { SECRET_API_KEY } from 'astro:env/server';
Defensive patterns

Strategy: validation

Validate before calling

// keep vite.envPrefix and env.schema disjoint for secrets
// env.schema keys with access 'secret' must not start with any envPrefix entry
const envPrefix = ['PUBLIC_'];
const schema = { SECRET_API_KEY: 'secret', PUBLIC_SITE_URL: 'public' };
const conflicts = Object.entries(schema)
  .filter(([k, access]) => access === 'secret' && envPrefix.some((p) => k.startsWith(p)))
  .map(([k]) => k);
if (conflicts.length) throw new Error('secret/prefix conflicts: ' + conflicts.join(', '));

Prevention

When it happens

Trigger: Setting `vite.envPrefix: ['SECRET_']` (or any prefix that matches a secret's name) while env.schema declares e.g. SECRET_API_KEY with access 'secret'; using an empty-string prefix `''`, which matches every variable name, in a project that declares any secret.

Common situations: Adopting astro:env while keeping legacy import.meta.env.SECRET_* usage via a broad envPrefix; copy-pasting Vite envPrefix configuration into an Astro project that also uses env.schema; enabling a wide prefix for convenience without checking the schema for secrets.

Related errors


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

Appendix: source

Thrown at packages/astro/src/env/validators.ts:210

	const schema = config.env.schema;
	const envPrefix = config.vite?.envPrefix;

	// No schema or using default prefix — nothing to validate
	if (Object.keys(schema).length === 0 || !envPrefix) {
		return;
	}

	const prefixes = Array.isArray(envPrefix) ? envPrefix : [envPrefix];
	const conflicts: string[] = [];

	for (const [key, options] of Object.entries(schema)) {
		if (options.access === 'secret' && prefixes.some((prefix) => key.startsWith(prefix))) {
			conflicts.push(key);
		}
	}

	if (conflicts.length > 0) {
		throw new AstroError({
			...AstroErrorData.EnvPrefixConflictsWithSecret,
			message: AstroErrorData.EnvPrefixConflictsWithSecret.message(conflicts),
		});
	}
}

View on GitHub (pinned to 52e6c34790)