can1357/oh-my-pi · error · Error

Failed to move artifacts and rollback: ${rollbackErr instanc

Error message

Failed to move artifacts and rollback: ${rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr)}

What it means

During a session directory move, artifacts (session artifacts directory) are relocated first. If the move then fails and a rollback of the artifacts directory via fs.rename back to its original location also fails, this combined error is thrown reporting the rollback failure. It means the artifacts directory is now in the NEW location and could not be restored, leaving the session split across old and new locations.

Source

Thrown at packages/coding-agent/src/session/session-manager.ts:1599

					}

					if (artifactPathChanged) {
						try {
							const artifactStat = await fs.promises.stat(oldArtifactsDir);
							if (artifactStat.isDirectory()) {
								await fs.promises.rename(oldArtifactsDir, newArtifactsDir);
								artifactsMoved = true;
							}
						} catch (err) {
							if (!isEnoent(err)) throw err;
						}
					}
				} catch (err) {
					if (artifactsMoved && oldArtifactsDir && newArtifactsDir) {
						try {
							await fs.promises.rename(newArtifactsDir, oldArtifactsDir);
						} catch (rollbackErr) {
							throw new Error(
								`Failed to move artifacts and rollback: ${rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr)}`,
							);
						}
					}

					if (sessionMoved) {
						try {
							await fs.promises.rename(newSessionFile, oldSessionFile);
						} catch (rollbackErr) {
							throw new Error(
								`Failed to move session file and rollback: ${rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr)}`,
							);
						}
					}

					throw err;
				}

View on GitHub (pinned to 9690622007)

Solutions

  1. Manually rename the artifacts directory from the new location back to oldArtifactsDir using the inner error message for the cause.
  2. Remove whatever now occupies oldArtifactsDir (if stale) and retry the rollback.
  3. Copy-then-delete instead of rename if the locations are on different filesystems (EXDEV).
  4. Re-run the move operation to a consistent state, ensuring no concurrent process touches the session directories.

Example fix

// before: blind rename back fails on EXDEV
await fs.promises.rename(newArtifactsDir, oldArtifactsDir);
// after
try {
  await fs.promises.rename(newArtifactsDir, oldArtifactsDir);
} catch {
  await cp(newArtifactsDir, oldArtifactsDir, { recursive: true });
  await fs.promises.rm(newArtifactsDir, { recursive: true });
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (existsSync(oldArtifactsDir)) {
  throw new Error("Old artifacts dir already exists; move aborted beforehand");
}

Type guard

null

Try / catch

try {
  await mgr.moveSession(newCwd);
} catch (err) {
  if (err.message.includes("Failed to move artifacts and rollback")) {
    // artifacts are at the NEW location; relocate manually per err.message
  } else throw err;
}

Prevention

When it happens

Trigger: Move-session operation throws after artifactsMoved is true, and the compensating rename(newArtifactsDir, oldArtifactsDir) fails — e.g. oldArtifactsDir was recreated in the meantime, destination is on another filesystem, or permissions deny the rename.

Common situations: Another process recreated the old directory during the move; cross-device rename (EXDEV); read-only parent directory; antivirus/file lock on Windows holding the directory.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/d1d237bce431e3a1. Report an issue: GitHub.