babel/babel · critical · ConfigError

Expected config object but found array

Error message

Expected config object but found array

What it means

Thrown by Babel's package.json reader (makeStaticFileCache callback in package.ts) after JSON.parse succeeds but the resulting value is a JSON array. A package.json file must be a JSON object at the top level; an array is structurally invalid and Babel cannot derive package metadata (dirname, babelrc search boundaries) from it. The check guards the very first step of config resolution, where Babel walks up directories looking for the owning package.

Source

Thrown at packages/babel-core/src/config/files/package.ts:32

    try {
      options = JSON.parse(content) as unknown;
    } catch (err) {
      throw new ConfigError(
        `Error while parsing JSON - ${err.message}`,
        filepath,
      );
    }

    if (!options) throw new Error(`${filepath}: No config detected`);

    if (typeof options !== "object") {
      throw new ConfigError(
        `Config returned typeof ${typeof options}`,
        filepath,
      );
    }
    if (Array.isArray(options)) {
      throw new ConfigError(`Expected config object but found array`, filepath);
    }

    return {
      filepath,
      dirname: path.dirname(filepath),
      options,
    };
  },
);

/**
 * Find metadata about the package that this file is inside of. Resolution
 * of Babel's config requires general package information to decide when to
 * search for .babelrc files
 */
export function* findPackageData(filepath: string): Handler<FilePackageData> {
  let pkg = null;
  const directories = [];

View on GitHub (pinned to 06b6eae39d)

Solutions

  1. Open the package.json named in the ConfigError filepath and confirm its top-level value is an object `{ ... }`, not an array.
  2. Restore package.json from version control or regenerate it via `npm init` if the contents are unrecoverable.
  3. Run the file through a JSON linter (`npx jsonlint package.json`) to catch structural corruption.
  4. Check for stray symlinks or monorepo hoisting that may have replaced the real package.json with another file.

Example fix

// before: package.json contains
["@babel/core", "webpack"]
// after: package.json contains
{
  "name": "my-package",
  "version": "1.0.0"
}
Defensive patterns

Strategy: validation

Validate before calling

const fs = require('fs');
function validatePackageJson(filepath) {
  const parsed = JSON.parse(fs.readFileSync(filepath, 'utf8'));
  if (Array.isArray(parsed)) throw new Error(`${filepath}: package.json must be an object, got array`);
  if (parsed === null || typeof parsed !== 'object') throw new Error(`${filepath}: package.json must be an object`);
  return parsed;
}

Type guard

function isValidPackageJson(v): v is Record<string, unknown> {
  return v !== null && typeof v === 'object' && !Array.isArray(v);
}

Try / catch

try {
  babel.transformSync(code, { filename });
} catch (e) {
  if (e.message.includes('Expected config object but found array')) {
    console.error('package.json is corrupt; restore from VCS:', e.message);
  } else throw e;
}

Prevention

When it happens

Trigger: Babel calls findPackageData -> readConfigPackage while locating the package that owns a file being compiled. If the nearest package.json parses to a JSON array (e.g. file content is literally `[]` or `["a","b"]`), the Array.isArray(options) branch at package.ts:31 fires and a ConfigError wrapping this message is thrown with the offending filepath.

Common situations: A package.json was overwritten or corrupted (e.g. a tool wrote a JSON array of dependency names into it), a symlink points at the wrong file, a monorepo hoisting accident left a stub package.json containing an array, or a hand-edited/package-generator script emitted malformed JSON. Also seen when a config file masquerading as package.json is mistakenly named package.json.

Related errors


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