can1357/oh-my-pi · error · Error
archive destination exists: ${destSession}
Error message
archive destination exists: ${destSession} What it means
moveSessionWithArtifacts archives a session file by gzipping it to a destination path. Before any move it checks that neither the destination nor its legacy (.gz-less or .gz-suffixed) counterpart exists, throwing this error to avoid silently overwriting an existing archive.
Source
Thrown at packages/coding-agent/src/cli/gc-cli.ts:526
if (renamed) await fs.rm(destination, { force: true });
throw error;
}
}
async function restoreGzipSessionFile(source: string, destination: string): Promise<void> {
await fs.mkdir(path.dirname(destination), { recursive: true });
const decompressed = gunzipSync(await Bun.file(source).bytes());
await Bun.write(destination, decompressed);
await fs.unlink(source);
}
async function moveSessionWithArtifacts(candidate: ArchiveCandidate): Promise<void> {
const sourceSession = candidate.session.path;
const destSession = candidate.destinationPath;
const legacyDestSession = destSession.endsWith(".gz") ? destSession.slice(0, -".gz".length) : `${destSession}.gz`;
const sourceArtifacts = sessionArtifactsPath(sourceSession);
const destArtifacts = sessionArtifactsPath(destSession);
if (await pathExists(destSession)) throw new Error(`archive destination exists: ${destSession}`);
if (await pathExists(legacyDestSession)) throw new Error(`archive destination exists: ${legacyDestSession}`);
if ((await pathExists(sourceArtifacts)) && (await pathExists(destArtifacts))) {
throw new Error(`archive artifacts destination exists: ${destArtifacts}`);
}
const moved: Array<{ source: string; destination: string; compressed?: boolean }> = [];
try {
await gzipSessionFile(sourceSession, destSession);
moved.push({ source: sourceSession, destination: destSession, compressed: true });
if (await pathExists(sourceArtifacts)) {
await movePath(sourceArtifacts, destArtifacts);
moved.push({ source: sourceArtifacts, destination: destArtifacts });
}
} catch (error) {
for (const move of moved.reverse()) {
try {
if (move.compressed) {
await restoreGzipSessionFile(move.destination, move.source);View on GitHub (pinned to 9690622007)
Solutions
- Inspect the destination path in the message; delete or rename the stale archive if it is no longer needed, then re-run GC
- Ensure only one GC process runs at a time (the GC lock should enforce this — check for stale locks)
- If the collision is systematic, fix the destination-naming logic so archived sessions get unique destinations
Example fix
// before mv session.jsonl session.jsonl.gz // fails if destination exists // after rm stale-session.jsonl.gz && omp gc // clear stale archive, then retry
Defensive patterns
Strategy: validation
Validate before calling
import * as fs from "node:fs/promises";
const dest = computeDestinationPath(sessionPath);
for (const p of [dest, dest.replace(/\.gz$/, "")]) {
try { await fs.access(p); throw new Error(`destination exists: ${p}`); }
catch (e) { if ((e as NodeJS.ErrnoException).code !== "ENOENT") throw e; }
} Try / catch
try {
await archiveSession(candidate);
} catch (err) {
if (err instanceof Error && err.message.startsWith("archive destination exists:")) {
const dest = err.message.split("archive destination exists: ")[1];
// inspect/resolve the collision (delete stale archive or pick a new destination) before retrying
} else throw err;
} Prevention
- Run only one GC at a time and respect the GC lock
- Clean up after interrupted archive runs before re-running GC
- Verify prior-run state (destinations already written) before retrying an archive batch
When it happens
Trigger: Archiving a session when candidate.destinationPath already exists on disk — e.g. re-running GC after a previous partial archive, or two sessions resolving to the same destination name.
Common situations: Repeated `omp gc`/stats-cleanup runs where a prior run crashed after writing the destination; session files renamed/copied so their computed destination collides; concurrent GC invocations racing on the same session.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- archive destination exists: ${legacyDestSession}
- archive artifacts destination exists: ${destArtifacts}
- Unable to allocate unique backup path in ${destinationDir}
- Unable to read ARJ archive: ${error instanceof Error ? error
- unknown filetype: {ft_debug}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/80e9ca635c9c51ad.
Report an issue: GitHub.