can1357/oh-my-pi · error · Error

Failed to move session file and rollback: ${rollbackErr inst

Error message

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

What it means

Sibling of error 2074: after artifacts rollback, if the session file itself had already been moved (sessionMoved true) and the compensating rename(newSessionFile, oldSessionFile) fails, this error is thrown reporting that the session file could not be restored. The original triggering error is then also thrown. The session file ends up at the new location while the caller believes the move was aborted.

Source

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

							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;
				}

				if (sessionFileExisted && sessionPathChanged) {
					this.#header.previousSessionFiles = [
						...new Set([...(this.#header.previousSessionFiles ?? []), path.resolve(oldSessionFile)]),
					];
				}

				this.#sessionFile = newSessionFile;
				this.#artifactManager = null;
				this.#artifactManagerSessionFile = null;
				// Path is repointed; hot-path appends may use `#sessionFile` again.

View on GitHub (pinned to 9690622007)

Solutions

  1. Manually move newSessionFile back to oldSessionFile (inner error names the cause).
  2. Close processes locking the file (editors, sync clients) and retry the rollback.
  3. If the old location is unrecoverable, accept the new location and update breadcrumbs/state accordingly.
  4. Use copy+delete as fallback when rename fails with EXDEV.

Example fix

// before
await fs.promises.rename(newSessionFile, oldSessionFile); // fails if locked
// after
closeHoldingProcesses();
try {
  await fs.promises.rename(newSessionFile, oldSessionFile);
} catch (e) {
  if (e.code === "EXDEV") {
    await copyFile(newSessionFile, oldSessionFile);
    await unlink(newSessionFile);
  } else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (existsSync(oldSessionFile)) {
  throw new Error("Old session file still present; cannot roll back onto it");
}

Type guard

null

Try / catch

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

Prevention

When it happens

Trigger: Move-session operation fails after the session file rename succeeded, and rollback rename of the session file fails — destination occupied, cross-device move, permissions, or file locked by another process.

Common situations: Editor/indexer holding the session file open on Windows; old path recreated concurrently; EXDEV across mount points; read-only directory.

Related errors


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