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 for

View on GitHub (pinned to 06b6eae39d)

Solutions

  1. 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.
  2. Use api.cache.never() only if you accept the performance cost of re-running the factory per file.
  3. 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

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


AI-assisted analysis of babel/babel@06b6eae39d (2026-08-03). Data as JSON: /data/errors/da992c42ac30e724.json. Report an issue: GitHub.