expo/expo · error · CommandError

--output-dir cannot be the same as the project directory.

Error message

--output-dir cannot be the same as the project directory.

What it means

Thrown by `exportAsync` when the resolved `--output-dir` equals the project root directory. Writing the export into the project root would clobber source files and create a recursive delete. A `CommandError` (no stack) prevents a destructive operation. The check uses `path.resolve(projectRoot, options.outputDir)` so relative paths are resolved before comparison.

Source

Thrown at packages/@expo/cli/src/export/exportAsync.ts:18

import chalk from 'chalk';
import path from 'path';

import * as Log from '../log';
import { waitUntilAtlasExportIsReadyAsync } from '../start/server/metro/debugging/attachAtlas';
import { FileNotifier } from '../utils/FileNotifier';
import { ensureDirectoryAsync, removeAsync } from '../utils/dir';
import { CommandError } from '../utils/errors';
import { ensureProcessExitsAfterDelay } from '../utils/exit';
import { exportAppAsync } from './exportApp';
import type { Options } from './resolveOptions';

export async function exportAsync(projectRoot: string, options: Options) {
  // Ensure the output directory is created
  const outputPath = path.resolve(projectRoot, options.outputDir);

  if (outputPath === projectRoot) {
    throw new CommandError('--output-dir cannot be the same as the project directory.');
  } else if (projectRoot.startsWith(outputPath)) {
    throw new CommandError(`--output-dir cannot be a parent directory of the project directory.`);
  }
  // Delete the output directory if it exists
  await removeAsync(outputPath);
  // Create the output directory
  await ensureDirectoryAsync(outputPath);

  // Export the app
  await exportAppAsync(projectRoot, options);

  // Stop any file watchers to prevent the CLI from hanging.
  FileNotifier.stopAll();
  // Wait until Atlas is ready, when enabled
  // NOTE(cedric): this is a workaround, remove when `process.exit` is removed
  await waitUntilAtlasExportIsReadyAsync(projectRoot);

  // Final notes

View on GitHub (pinned to b09195aac2)

Solutions

  1. Point `--output-dir` at a distinct subfolder, e.g. `--output-dir dist`.
  2. If scripting, validate the resolved path is not the project root before invoking the CLI.
  3. Avoid `.` and absolute project-root paths for `--output-dir`.

Example fix

// before
expo export --output-dir .

// after
expo export --output-dir dist
Defensive patterns

Strategy: validation

Validate before calling

import path from 'path';
function assertSafeOutputDir(projectRoot: string, outputDir: string): void {
  const out = path.resolve(projectRoot, outputDir);
  if (out === path.resolve(projectRoot)) {
    throw new Error('--output-dir must not equal the project root');
  }
  if (path.resolve(projectRoot).startsWith(out + path.sep)) {
    throw new Error('--output-dir must not be an ancestor of the project root');
  }
}

Prevention

When it happens

Trigger: Running `expo export --output-dir .` or `--output-dir <absolute project root>`. Also if `outputDir` defaults to a value that, after resolution, lands on the project root.

Common situations: CI scripts that parameterise the output dir and pass `.` or an empty string. Confusion between the project root and the desired `dist` folder. Copy-pasting a command template without filling in the path.

Related errors


AI-assisted analysis of expo/expo@b09195aac2 (2026-08-12). Data as JSON: /api/errors/d3f2f8f51713c3cf. Report an issue: GitHub.