Fission-AI/OpenSpec · error

Could not retire capability

Error message

Could not retire capability '${update.id}': could not verify ${update.target} before deletion (${error instanceof Error ? error.message : String(error)}).

What it means

Thrown during spec retirement (archive): the pre-deletion verification step — lstat/realpath on the target file — failed for a reason other than ENOENT (permissions, I/O errors). Deletion cannot proceed safely because the code cannot confirm what it is about to unlink; the wrapped cause is included.

Solutions

  1. Check file permissions and filesystem health at the reported target path
  2. Resolve the underlying verification error described in the message, then rerun the archive
  3. Retry the archive once transient filesystem errors are ruled out
Defensive patterns

Strategy: try-catch

When it happens

Trigger: Thrown at src/core/specs-apply.ts:808 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/326ef78cf53b4563. Report an issue: GitHub.

Appendix: source

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

    verifyDisplaced?: (displacedPath: string) => Promise<void>;
    deferDelete?: boolean;
  } = {}
): Promise<{ retired: boolean; resolvedPath?: string; displacedPath?: string }> {
  if (options.deferDelete && options.verifyDisplaced === undefined) {
    throw new Error('Deferred retirement requires displaced-file verification.');
  }
  // Resolved before the unlink, while the link still exists, so the report can
  // name the file that actually goes when a symlink points out of the tree.
  // A symlinked `spec.md` is excluded: `realpath` would follow it, but `unlink`
  // removes the link and leaves the target alone, so naming the target would
  // claim a file was deleted that is still there.
  let realSource: string | undefined;
  try {
    const link = await fs.lstat(update.target);
    realSource = link.isSymbolicLink() ? undefined : await fs.realpath(update.target);
  } catch (error) {
    if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { retired: false };
    throw new Error(
      `Could not retire capability '${update.id}': could not verify ${update.target} ` +
        `before deletion (${error instanceof Error ? error.message : String(error)}).`
    );
  }

  if (realSource !== undefined) {
    let inside: boolean;
    try {
      inside = await isInsideRealDir(realSource, mainSpecsDir);
    } catch (error) {
      throw new Error(
        `Could not retire capability '${update.id}': could not verify that ${update.target} ` +
          `is inside ${mainSpecsDir} (${error instanceof Error ? error.message : String(error)}).`
      );
    }
    if (!inside) {
      throw new Error(
        `Could not retire capability '${update.id}': ${update.target} resolves outside ` +

View on GitHub (pinned to 6926ccb18a)