sveltejs/kit · error · Error

Cannot import `$app/*` modules other than `$app/env` inside

Error message

Cannot import `$app/*` modules other than `$app/env` inside `src/env`

What it means

Modules in src/env can only import `$app/env`; importing any other `$app/*` module (like $app/navigation or $app/stores) is prohibited because src/env.js is loaded in a build-time context without the full app runtime. When the dynamic import fails with ERR_MODULE_NOT_FOUND for a `$app` module, SvelteKit rethrows with this clearer message.

Source

Thrown at packages/kit/src/core/env.js:103

		if (!variables || typeof variables !== 'object') {
			throw new Error(`${file} must export a variables object`);
		}

		// validate
		for (const name of Object.keys(variables)) {
			if (!valid_identifier.test(name) || reserved.has(name)) {
				throw new Error(`Invalid environment variable name ${JSON.stringify(name)}`);
			}
		}
	} catch (e) {
		const error = /** @type {any} */ (e || {});

		if (
			error.code === 'ERR_MODULE_NOT_FOUND' &&
			error.message?.includes(`Cannot find module '$app`)
		) {
			throw new Error(
				`Cannot import \`$app/*\` modules other than \`$app/env\` inside \`src/env\``,
				{ cause: e }
			);
		}

		throw error;
	} finally {
		await server.close();
	}

	return { variables, deps };
}

/**
 * Creates the `<sveltekit:generated>/env/*` modules, keyed by path relative to `dir`. Every module
 * derives from one pass over `variables`, so an inlined value is validated once per build.
 * @param {ValidatedConfig} config
 * @param {Record<string, EnvVarConfig<any>> | null} variables

View on GitHub (pinned to 03f1687fe6)

Solutions

  1. Remove all `$app/*` imports other than `$app/env` from src/env.js and everything it imports.
  2. Move shared logic that needs $app modules out of the src/env import graph.
  3. Use only plain Node-compatible code and $app/env inside src/env.js.

Example fix

// before (src/env.js)
import { dev } from '$app/environment';
import { goto } from '$app/navigation';
export const variables = {};
// after
import { dev } from '$app/environment';
export const variables = {};
Defensive patterns

Strategy: validation

Validate before calling

// scan src/env.js and its local imports for forbidden $app modules
const src = await fs.readFile('src/env.js', 'utf8');
const bad = src.match(/\$app\/(?!env\b)[\w-]+/g);
if (bad) throw new Error(`Forbidden imports in src/env.js: ${bad.join(', ')}`);

Try / catch

try {
  await build();
} catch (e) {
  if (String(e.message).includes('Cannot import `$app/*` modules')) {
    console.error('src/env.js import graph must only use $app/env — refactor the import out');
  } else throw e;
}

Prevention

When it happens

Trigger: src/env.js (or its imports) containing `import { goto } from '$app/navigation'` or any non-env `$app` import; transitively importing an app module that itself imports $app/* modules.

Common situations: Reusing a shared module inside src/env.js that pulls in client-side app code; attempting to derive env validation from app stores.

Related errors


AI-assisted analysis of sveltejs/kit@03f1687fe6 (2026-09-02). Data as JSON: /api/errors/d2e4fa07080a0ea6. Report an issue: GitHub.