google-gemini/gemini-cli · error · Error

EEXIST

EEXIST

Error message

Slug ${slug} is already owned by ${owner}

What it means

EEXIST 'Slug X is already owned by Y' is thrown by ensureOwnershipMarkers when a project-slug directory's PROJECT_ROOT_FILE marker already exists and points to a DIFFERENT normalized project path. The project registry uses these markers to map short slugs to absolute project paths; a mismatch means another project claimed the slug first.

Source

Thrown at packages/core/src/config/projectRegistry.ts:391

    slug: string,
    projectPath: string,
  ): Promise<void> {
    const normalizedProject = this.normalizePath(projectPath);
    for (const baseDir of this.baseDirs) {
      const slugDir = path.join(baseDir, slug);
      if (!fs.existsSync(slugDir)) {
        await fs.promises.mkdir(slugDir, { recursive: true });
      }
      const markerPath = path.join(slugDir, PROJECT_ROOT_FILE);
      if (fs.existsSync(markerPath)) {
        const owner = (await fs.promises.readFile(markerPath, 'utf8')).trim();
        if (this.normalizePath(owner) === normalizedProject) {
          continue;
        }
        // Collision!
        const error = Object.assign(
          new Error(`Slug ${slug} is already owned by ${owner}`),
          { code: 'EEXIST' },
        );
        throw error;
      }
      // Use flag: 'wx' to ensure atomic creation
      try {
        await fs.promises.writeFile(markerPath, normalizedProject, {
          encoding: 'utf8',
          flag: 'wx',
        });
      } catch (e: unknown) {
        if (isNodeError(e) && e.code === 'EEXIST') {
          // Re-verify ownership in case we just lost a race
          const owner = (await fs.promises.readFile(markerPath, 'utf8')).trim();
          if (this.normalizePath(owner) === normalizedProject) {
            continue;
          }
        }
        throw e;

View on GitHub (pinned to 5024443c72)

Solutions

  1. Let the caller retry — getOrAssignSlug already advances to the next candidate on EEXIST; the error should be rare to surface.
  2. Delete the stale marker directory (<baseDir>/<slug>/PROJECT_ROOT_FILE) for the old owner if the project was renamed/moved.
  3. Rename one project so its slugify() output is unique.

Example fix

# before: marker owned by /old/path
rm -rf ~/.gemini/registry/my-app
# after: next setupUser call reclaims the slug cleanly
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check ownership before ensureOwnershipMarkers
for (const base of baseDirs) {
  const marker = path.join(base, slug, PROJECT_ROOT_FILE);
  if (fs.existsSync(marker) && fs.readFileSync(marker,'utf8').trim() !== normalize(projectPath)) {
    return pickNextCandidate();
  }
}

Type guard

function isSlugCollision(e: unknown): boolean {
  return isNodeError(e) && (e as NodeJS.ErrnoException).code === 'EEXIST'
    || (e instanceof Error && e.message.includes('already owned by'));
}

Try / catch

try { await registry.ensureOwnershipMarkers(slug, projectPath); }
catch (e) {
  if (isSlugCollision(e)) { /* try next candidate slug */ continue; }
  throw e;
}

Prevention

When it happens

Trigger: ensureOwnershipMarkers(slug, projectPath) reads markerPath; reads owner from it; normalizePath(owner) !== normalizePath(projectPath) -> throws Error with code:'EEXIST' at line 389-393. The caller (getOrAssignSlug) catches code==='EEXIST' or message includes 'already owned by' and retries the next candidate slug.

Common situations: Two sibling projects with similar directory names slugify to the same string (e.g. 'my-app' and 'My App'); stale marker left by a moved/renamed project directory; race when two CLI processes onboard different projects concurrently.

Related errors


AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12). Data as JSON: /api/errors/93fe4d047f4a7aec. Report an issue: GitHub.