jackwener/OpenCLI · warning

Failed to prune trace artifact ${dir}: ${err instanceof Erro

Error message

Failed to prune trace artifact ${dir}: ${err instanceof Error ? err.message : String(err)}

What it means

pruneTraceArtifacts deletes old trace artifact directories with fs.rmSync after deciding which entries exceed retention limits. When the OS refuses the recursive removal (permissions, files locked by another process, EPERM/EBUSY/EACCES), the error is caught per-directory and surfaced as this warning while other directories continue to be pruned. It is non-fatal: the affected dir simply stays on disk and is reported as kept.

Source

Thrown at src/observation/retention.ts:127

    remaining = remaining.filter((entry) => entry.dir !== victim.dir);
  }

  let remainingBytes = remaining.reduce((sum, entry) => sum + entry.sizeBytes, 0);
  while (remainingBytes > policy.maxBytesPerProfile) {
    const victim = remaining.find((entry) => !entry.protected);
    if (!victim) break;
    deleted.add(victim.dir);
    remaining = remaining.filter((entry) => entry.dir !== victim.dir);
    remainingBytes -= victim.sizeBytes;
  }

  const deletedDirs: string[] = [];
  for (const dir of sorted.map((entry) => entry.dir).filter((dir) => deleted.has(dir))) {
    try {
      fs.rmSync(dir, { recursive: true, force: true });
      deletedDirs.push(dir);
    } catch (err) {
      warn(`Failed to prune trace artifact ${dir}: ${err instanceof Error ? err.message : String(err)}`);
    }
  }

  const keptEntries = entries.filter((entry) => !deletedDirs.includes(entry.dir));
  return {
    scanned: entries.length,
    deleted: deletedDirs,
    kept: keptEntries.map((entry) => entry.dir),
    totalBytesBefore,
    totalBytesAfter: keptEntries.reduce((sum, entry) => sum + entry.sizeBytes, 0),
  };
}

function readTraceEntries(
  tracesDir: string,
  protectedDirs: Set<string>,
  warn: (message: string) => void,
): TraceEntry[] {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Check permissions on the failing directory (ls -la) and chown/chmod so the process user can delete it.
  2. Close processes holding files open in the traces directory (or retry later — the next prune pass will retry).
  3. On Windows, exclude the traces directory from antivirus/sync tools that lock files.
  4. If a specific dir persistently fails, delete it manually and re-run the retention pass.

Example fix

// before
fs.rmSync(dir, { recursive: true, force: true });
// after
try {
  fs.rmSync(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
} catch (err) {
  fs.chmodSync(dir, 0o700);
  fs.rmSync(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
}
Defensive patterns

Strategy: try-catch

Validate before calling

const st = fs.statSync(dir);
if (st.isDirectory() && fs.accessSync(dir, fs.constants.W_OK)) throw new Error(`no write access: ${dir}`);

Type guard

function isRemovable(dir: string): boolean {
  try { fs.accessSync(dir, fs.constants.W_OK); return fs.statSync(dir).isDirectory(); } catch { return false; }
}

Try / catch

try {
  fs.rmSync(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
} catch (err) {
  const code = (err as NodeJS.ErrnoException).code;
  console.warn(`skip ${dir}: ${code ?? err}`); // EPERM/EBUSY -> retry later
}

Prevention

When it happens

Trigger: fs.rmSync(dir, {recursive:true, force:true}) throws for a specific trace directory — e.g. the directory contains read-only files, is held open by another process (a running trace viewer, editor, antivirus on Windows), or the current user lacks write permission on a parent.

Common situations: Running retention cleanup while a debugger or trace tool has files open; traces directory owned by a different user (CI artifacts written as root, pruned as non-root); Windows antivirus or OneDrive sync locking files; NFS/EBS stale handles.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/d97f76a5c5d50d3a. Report an issue: GitHub.