What Causes This Error?
This error occurs when your environment variables don’t match the schema you’ve defined in src/env.d.ts or when required variables are missing. Astro’s env validation helps catch configuration issues early.
The Problem
// src/env.d.ts
interface ImportMetaEnv {
readonly PUBLIC_API_URL: string; // Required
readonly API_KEY: string; // Required
}
# .env - Missing API_KEY
PUBLIC_API_URL=https://api.example.com
# API_KEY is not set!
The Fix
Provide Required Variables
# .env
PUBLIC_API_URL=https://api.example.com
API_KEY=your-api-key-here
Common Scenarios
Define Environment Schema
// src/env.d.ts
/// <reference types="astro/client" />
interface ImportMetaEnv {
// Public variables (exposed to client)
readonly PUBLIC_API_URL: string;
readonly PUBLIC_SITE_NAME: string;
// Private variables (server-only)
readonly DATABASE_URL: string;
readonly API_SECRET: string;
// Optional with defaults
readonly PORT?: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
Using astro:env (Astro v4.10+)
// src/env.d.ts
/// <reference types="astro/client" />
declare module 'astro:env/server' {
export const DATABASE_URL: string;
export const API_SECRET: string;
}
declare module 'astro:env/client' {
export const PUBLIC_API_URL: string;
}
// astro.config.mjs
import { defineConfig, envField } from 'astro/config';
export default defineConfig({
experimental: {
env: {
schema: {
DATABASE_URL: envField.string({
context: 'server',
access: 'secret',
}),
PUBLIC_API_URL: envField.string({
context: 'client',
access: 'public',
}),
PORT: envField.number({
context: 'server',
access: 'public',
optional: true,
default: 3000,
}),
},
},
},
});
Environment Files
# .env (all environments)
PUBLIC_SITE_NAME=My Site
# .env.development (dev only)
PUBLIC_API_URL=http://localhost:3001/api
DATABASE_URL=postgresql://localhost/dev
# .env.production (production only)
PUBLIC_API_URL=https://api.example.com
DATABASE_URL=postgresql://prod-server/db
Check Required Variables
// src/utils/env.ts
export function validateEnv() {
const required = [
'PUBLIC_API_URL',
'DATABASE_URL',
'API_SECRET',
];
const missing = required.filter(key => !import.meta.env[key]);
if (missing.length > 0) {
throw new Error(`Missing environment variables: ${missing.join(', ')}`);
}
}
Handling Optional Variables
// astro.config.mjs with optional fields
export default defineConfig({
experimental: {
env: {
schema: {
// Required - will error if missing
DATABASE_URL: envField.string({
context: 'server',
access: 'secret',
}),
// Optional with default
LOG_LEVEL: envField.string({
context: 'server',
access: 'public',
optional: true,
default: 'info',
}),
// Optional, may be undefined
ANALYTICS_ID: envField.string({
context: 'client',
access: 'public',
optional: true,
}),
},
},
},
});
Type Validation
// astro.config.mjs
export default defineConfig({
experimental: {
env: {
schema: {
// String validation
NODE_ENV: envField.enum({
context: 'server',
access: 'public',
values: ['development', 'production', 'test'],
}),
// Number validation
MAX_ITEMS: envField.number({
context: 'server',
access: 'public',
min: 1,
max: 100,
}),
// Boolean
ENABLE_CACHE: envField.boolean({
context: 'server',
access: 'public',
default: false,
}),
},
},
},
});
Using Environment Variables
---
// Server-side access
const dbUrl = import.meta.env.DATABASE_URL;
// With astro:env
import { DATABASE_URL } from 'astro:env/server';
import { PUBLIC_API_URL } from 'astro:env/client';
---
<!-- Client-side only PUBLIC_ variables -->
<script>
// Only PUBLIC_ prefixed vars are available
const apiUrl = import.meta.env.PUBLIC_API_URL;
// ❌ This won't work (not prefixed with PUBLIC_)
// const secret = import.meta.env.DATABASE_URL;
</script>
Debug Environment
# Check loaded variables
npx astro info
# List env files
ls -la .env*
# Verify variable is set
echo $DATABASE_URL
Production Deployment
# Vercel
vercel env add DATABASE_URL
# Netlify
netlify env:set DATABASE_URL "value"
# Most platforms: use dashboard to set env vars
Don’t Commit Secrets
# .gitignore
.env
.env.local
.env.*.local
.env.production
# Only commit example
!.env.example
# .env.example (template for required vars)
PUBLIC_API_URL=
DATABASE_URL=
API_SECRET=
Quick Checklist
- Define env schema in
src/env.d.tsor config - Create
.envfile with required variables - Use
PUBLIC_prefix for client-accessible vars - Set optional variables with defaults if needed
- Never commit
.envfiles with secrets - Configure env vars in deployment platform