yamadashy/repomix · error · RepomixError

Refusing to trust ${configName}: it resolves outside the clo

Error message

Refusing to trust ${configName}: it resolves outside the cloned repository.

What it means

After confirming the config is a regular file, assertConfigIsContained resolves both the config and the clone directory with fs.realpath and computes path.relative. If the config's real path is not inside the clone's real path (relative path starts with '..' or is absolute), trust is refused. This blocks containment escapes such as a regular-file config inside a symlinked subdirectory pointing out of the temp clone dir, and like error 30 it applies even with --force and in CI.

Source

Thrown at src/cli/prompts/remoteConfigTrustPrompt.ts:120

 * show the user must live in the tree we just cloned.
 */
const assertConfigIsContained = async (
  configPath: string,
  repoDir: string,
  deps: Pick<ConfirmRemoteConfigTrustDeps, 'lstat' | 'realpath'>,
): Promise<void> => {
  const configName = path.basename(configPath);
  const stats = await deps.lstat(configPath);
  if (stats.isSymbolicLink() || !stats.isFile()) {
    throw new RepomixError(
      `Refusing to trust ${configName}: the remote repository's config must be a regular file, not a symlink.`,
    );
  }

  const [realConfigPath, realRepoDir] = await Promise.all([deps.realpath(configPath), deps.realpath(repoDir)]);
  const relative = path.relative(realRepoDir, realConfigPath);
  if (relative.startsWith('..') || path.isAbsolute(relative)) {
    throw new RepomixError(`Refusing to trust ${configName}: it resolves outside the cloned repository.`);
  }
};

/**
 * Interactively confirm before a cloned remote repository's config is trusted
 * (via `--remote-trust-config` / `REPOMIX_REMOTE_TRUST_CONFIG`). Shows the config
 * that is about to run, then asks the user. Throws `OperationCancelledError` when
 * the user declines.
 *
 * Proceeds without prompting when: `--force` is passed, the shell is
 * non-interactive (CI/pipes — preserves the historical behavior so automations do
 * not hang), the remote's exact config is already trusted, an absolute `--config`
 * is in use, or the cloned repo has no config to load.
 */
export const confirmRemoteConfigTrust = async (
  options: ConfirmRemoteConfigTrustOptions,
  deps: ConfirmRemoteConfigTrustDeps = {
    findLocalConfigPath,

View on GitHub (pinned to f465ad9093)

Solutions

  1. Restructure the remote repo so the config physically lives inside the repository tree with no symlinked ancestor directories.
  2. Clone/inspect the repo and run `realpath` on the config's parent directories to find which link escapes the tree, then replace it with a real directory.
  3. If your environment uses symlinked temp dirs, ensure repoDir and the config are referenced consistently (usually a repo-layout problem, not an env problem).
  4. Bypass the remote repo's config entirely by passing `--config /absolute/path/to/your.config.json`.

Example fix

# before (in the remote repo)
config/ -> /shared/config   (symlinked dir)
config/repomix.config.json

# after
rm config && mkdir config
cp /shared/config/repomix.config.json config/repomix.config.json
git add config && git commit -m "un symlink config dir"
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'node:fs/promises';
import path from 'node:path';
const [realConfig, realRepo] = await Promise.all([fs.realpath(configPath), fs.realpath(repoDir)]);
const rel = path.relative(realRepo, realConfig);
if (rel.startsWith('..') || path.isAbsolute(rel)) {
  throw new Error(`${configPath} resolves outside ${repoDir}`);
}

Type guard

const isContained = (realConfigPath: string, realRepoDir: string): boolean => {
  const rel = path.relative(realRepoDir, realConfigPath);
  return !rel.startsWith('..') && !path.isAbsolute(rel);
};

Try / catch

try {
  await runRemotePack();
} catch (e) {
  if (e instanceof RepomixError && e.message.includes('resolves outside the cloned repository')) {
    console.error('Remote config escapes the clone dir; restructure the repo or use --config.');
  } else throw e;
}

Prevention

When it happens

Trigger: `repomix --remote <url>` where the config file's realpath resolves outside the cloned repository's realpath — e.g. the config sits in a directory that is a symlink to somewhere outside the clone, or an OS-level temp-dir alias makes realpath disagree. Thrown from confirmRemoteConfigTrust via assertConfigIsContained.

Common situations: A repo commits a symlinked directory (e.g. config -> ../../shared) containing repomix.config.json; a bind-mount or symlinked /tmp on macOS (/tmp -> /private/tmp) combined with inconsistent path inputs; exotic container mounts where the clone dir itself resolves oddly.

Related errors


AI-assisted analysis of yamadashy/repomix@f465ad9093 (2026-08-29). Data as JSON: /api/errors/a40526a5a20d8b71. Report an issue: GitHub.