babel/babel · error · ConfigError
Caching was left unconfigured. Babel's plugins, presets, and
Error message
Caching was left unconfigured. Babel's plugins, presets, and .babelrc.js files can be configured
for various types of caching, using the first param of their handler functions:
module.exports = function(api) {
// The API exposes the following:
// Cache the returned value forever and don't call this function again.
api.cache(true);
// Don't cache at all. Not recommended because it will be very slow.
api.cache(false);
// Cached based on the value of some function. If this function returns a value different from
// a previously-encountered value, the plugins will re-evaluate.
var env = api.cache(() => process.env.NODE_ENV);
// If testing for a specific env, we recommend specifics to avoid instantiating a plugin for
// any possible NODE_ENV value that might come up during plugin execution.
var isProd = api.cache(() => process.env.NODE_ENV === "production");
// .cache(fn) will perform a linear search though instances to find the matching plugin based
// based on previous instantiated plugins. If you want to recreate the plugin and discard the
// previous instance whenever something changes, you may use:
var isProd = api.cache.invalidate(() => process.env.NODE_ENV === "production");
// Note, we also expose the following more-verbose versions of the above examples:
api.cache.forever(); // api.cache(true)
api.cache.never(); // api.cache(false)
api.cache.using(fn); // api.cache(fn)
// Return the value that will be cached.
return { };
}; What it means
When a config is provided as a factory function (e.g. module.exports = function(api){...}), runConfig invokes it with a ConfigAPI whose cache is queryable. After the function returns, Babel checks `cache.configured()`; if the function never called any api.cache(...) method, throwConfigError fires with a verbose guide. This forces explicit cache declarations so Babel knows when to re-evaluate the factory, preventing accidental re-runs on every file.
Source
Thrown at packages/babel-core/src/config/files/configuration.ts:369
export function* resolveShowConfigPath(
dirname: string,
): Handler<string | null> {
const targetPath = process.env.BABEL_SHOW_CONFIG_FOR;
if (targetPath != null) {
const absolutePath = path.resolve(dirname, targetPath);
const stats = (yield* fs.stat(absolutePath))!;
if (!stats.isFile()) {
throw new Error(
`${absolutePath}: BABEL_SHOW_CONFIG_FOR must refer to a regular file, directories are not supported.`,
);
}
return absolutePath;
}
return null;
}
function throwConfigError(filepath: string): never {
throw new ConfigError(
`\
Caching was left unconfigured. Babel's plugins, presets, and .babelrc.js files can be configured
for various types of caching, using the first param of their handler functions:
module.exports = function(api) {
// The API exposes the following:
// Cache the returned value forever and don't call this function again.
api.cache(true);
// Don't cache at all. Not recommended because it will be very slow.
api.cache(false);
// Cached based on the value of some function. If this function returns a value different from
// a previously-encountered value, the plugins will re-evaluate.
var env = api.cache(() => process.env.NODE_ENV);
// If testing for a specific env, we recommend specifics to avoid instantiating a plugin forView on GitHub (pinned to 06b6eae39d)
Solutions
- Add an explicit cache call at the top of the factory, most commonly api.cache(true) for static configs or api.cache(() => process.env.NODE_ENV) for env-dependent ones.
- Use api.cache.never() only if you accept the performance cost of re-running the factory per file.
- If the config is truly static, prefer exporting a plain object instead of a function (object configs do not require cache configuration).
Example fix
// before - throws error 30
module.exports = function(api) {
return { presets: ['@babel/preset-env'] };
};
// after
module.exports = function(api) {
api.cache(true);
return { presets: ['@babel/preset-env'] };
}; Defensive patterns
Strategy: validation
Validate before calling
// Static analysis hint: ensure any config/plugin/preset factory calls api.cache // Runtime check is hard since cache.configured() is internal; rely on linting: // grep for `module.exports = function` in babel.config.* and assert api.cache usage.
Type guard
function isCacheConfiguredCall(src: string): boolean {
return /api\.cache(?:\.(?:forever|never|using|invalidate)|\s*\()/.test(src);
} Try / catch
try { babel.loadOptionsSync(); }
catch (err) {
if (/Caching was left unconfigured/.test(err.message)) {
console.error('Add api.cache(true) (or similar) inside the config factory');
}
throw err;
} Prevention
- Always add api.cache(...) as the first statement of any config/plugin/preset factory.
- Prefer a plain object config when no runtime logic is needed.
- Use a lint rule banning function config exports without an api.cache reference.
When it happens
Trigger: A babel.config.js or plugin/preset factory function that returns a config object without ever calling api.cache(true), api.cache(false), api.cache(fn), api.cache.forever(), api.cache.never(), or api.cache.using(fn).
Common situations: First-time authors of a JS config factory who copy the object form and forget the cache call; refactoring an object config into a function without adding cache semantics; plugins/presets written as functions omitting the cache directive.
Related errors
- You appear to be using an async configuration, which your cu
- Error while parsing config - ${err.message}
- No config detected
- Config returned typeof ${typeof options}
- Expected config object but found array
AI-assisted analysis of babel/babel@06b6eae39d (2026-08-03).
Data as JSON: /data/errors/da992c42ac30e724.json.
Report an issue: GitHub.