Fission-AI/OpenSpec · error

A concurrent file now occupies ; the displaced spec was…

Error message

${error instanceof Error ? error.message : String(error)} A concurrent file now occupies ${update.target}; the displaced spec was retained at ${displaced}.

What it means

Thrown in the rollback path of deferred retirement: displaced-file verification failed (original error included), and while restoring state a new file was found occupying the target path. The displaced spec is preserved at the .openspec-retire-* path so no data is lost; both the original failure and the collision are reported.

Solutions

  1. Inspect the retained displaced file (path in message) and the concurrent occupant, and merge them manually
  2. Stop the concurrent writer before rerunning the archive
  3. Address the underlying verification error stated first in the message
Defensive patterns

Strategy: try-catch

When it happens

Trigger: Thrown at src/core/specs-apply.ts:853 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of Fission-AI/OpenSpec@6926ccb18a (2026-08-25). Data as JSON: /api/errors/df172d54d15a981d. Report an issue: GitHub.

Appendix: source

Thrown at src/core/specs-apply.ts:853

    if (options.verifyDisplaced) {
      const displaced = `${update.target}.openspec-retire-${randomUUID()}`;
      displacedPath = displaced;
      await fs.rename(update.target, displaced);
      try {
        await options.verifyDisplaced(displaced);
        try {
          await fs.lstat(update.target);
          throw new Error(
            `A concurrent file appeared at ${update.target} while archive was retiring it.`
          );
        } catch (targetError) {
          if ((targetError as NodeJS.ErrnoException).code !== 'ENOENT') throw targetError;
        }
        if (!options.deferDelete) await fs.unlink(displaced);
      } catch (error) {
        try {
          await fs.lstat(update.target);
          throw new Error(
            `${error instanceof Error ? error.message : String(error)} ` +
              `A concurrent file now occupies ${update.target}; the displaced spec was retained at ${displaced}.`
          );
        } catch (targetError) {
          if ((targetError as NodeJS.ErrnoException).code !== 'ENOENT') throw targetError;
        }
        await fs.rename(displaced, update.target);
        throw error;
      }
    } else {
      await fs.unlink(update.target);
    }
  } catch (error) {
    if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { retired: false };
    // A bare errno here reads as an internal failure; say what was being
    // attempted so the message is actionable on its own.
    throw new Error(
      `Could not retire capability '${update.id}': failed to delete ${update.target} ` +

View on GitHub (pinned to 6926ccb18a)