expo/expo · error · Error

The prebuilt-metadata catalog requires an expo repository ch

Error message

The prebuilt-metadata catalog requires an expo repository checkout. Use the app-plan mode for standalone projects.

What it means

expo-modules-autolinking's prebuilt-metadata catalog has a repo-scanning mode (scanRepoPackagesAsync) that only works inside the expo/expo monorepo, because it scans <repoRoot>/packages. When findExpoRepoRoot() cannot locate an expo checkout above the working directory, it throws and tells you to use the app-plan mode instead.

Source

Thrown at packages/expo-modules-autolinking/src/prebuiltMetadata.ts:83

        .map((podName) => [podName, entries[podName]!])
    );
  });
}

/** The expo repository root when this package runs from its packages/ checkout
 * (its own location is the same anchor used for external-configs below). */
function findExpoRepoRoot(): string | null {
  const repoRoot = path.resolve(__dirname, '..', '..', '..');
  return fs.existsSync(path.join(repoRoot, 'packages', 'expo-modules-core', 'spm.config.json'))
    ? repoRoot
    : null;
}

/** Every package in the expo repository, whether or not an app depends on it. */
async function scanRepoPackagesAsync(): Promise<DiscoveredPackages> {
  const repoRoot = findExpoRepoRoot();
  if (!repoRoot) {
    throw new Error(
      'The prebuilt-metadata catalog requires an expo repository checkout. Use the app-plan mode for standalone projects.'
    );
  }
  return scanDependenciesInSearchPath(path.join(repoRoot, 'packages'));
}

/** Only the packages the app itself resolves. */
async function findAppPackagesAsync(
  appRoot: string,
  optionsLoader: LinkingOptionsLoader
): Promise<DiscoveredPackages> {
  return findModulesAsync({
    appRoot,
    autolinkingOptions: await optionsLoader.getPlatformOptions('apple'),
  });
}

function readJsonFile(filePath: string): any | null {

View on GitHub (pinned to 7da61120be)

Solutions

  1. Switch to the app-plan (dependency-based) resolution mode instead of the repo-scan/prebuilt-metadata mode for standalone projects.
  2. Run the tooling from inside a full expo/expo checkout so findExpoRepoRoot() finds the repository root.
  3. If in a fork, restore the monorepo root files (package.json/workspaces config) that identify the expo repo.
  4. Clone the full expo monorepo and run the metadata scan from its root.

Example fix

// before (standalone app repo)
const pkgs = await scanRepoPackagesAsync();

// after
const pkgs = await resolveAppPlanPackagesAsync(projectRoot); // app-plan mode
Defensive patterns

Strategy: fallback

Validate before calling

const fs = require('fs');
function isInExpoRepo(root = process.cwd()) {
  return fs.existsSync(`${root}/packages/expo/package.json`) ||
         fs.existsSync(`${root}/packages`);
}

Try / catch

let packages;
try {
  packages = await resolvePrebuiltMetadata();
} catch (e) {
  if (String(e.message).includes('requires an expo repository checkout')) {
    packages = await resolveAppPlanPackagesAsync(projectRoot); // fallback mode
  } else throw e;
}

Prevention

When it happens

Trigger: Invoking the prebuilt-metadata resolution (e.g. `packages` in repo-scan mode) from a standalone React Native project, a fork without the repo marker files, or from a directory outside any expo/expo clone.

Common situations: Running expo-modules-autolinking tooling from an app project that vendors only some packages; CI checkout of a single package without the monorepo; a shallow/partial clone that dropped the root marker files findExpoRepoRoot() looks for.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of expo/expo@7da61120be (2026-09-09). Data as JSON: /api/errors/12b3da919ec24743. Report an issue: GitHub.