can1357/oh-my-pi · error · ToolError

Checkpoint already completed; continue from the retained rew

Error message

Checkpoint already completed; continue from the retained rewind report instead of calling rewind again.

What it means

The checkpoint rewind tool refuses to run a second rewind because a rewind has already been completed in this session: the checkpoint state was consumed and cleared, and only the final rewind report is retained. The library throws this instead of silently re-rewinding, because rewinding twice could clobber work done after the first rewind. The agent should use the already-captured rewind report (e.g. for context replacement) rather than invoking the tool again.

Source

Thrown at packages/coding-agent/src/tools/checkpoint.ts:117

	constructor(private readonly session: ToolSession) {
		this.description = prompt.render(rewindDescription);
	}

	static createIf(session: ToolSession): RewindTool | null {
		return new RewindTool(session);
	}

	async execute(
		_toolCallId: string,
		params: RewindParams,
		_signal?: AbortSignal,
		_onUpdate?: AgentToolUpdateCallback<RewindToolDetails>,
		_context?: AgentToolContext,
	): Promise<AgentToolResult<RewindToolDetails>> {
		if (!this.session.getCheckpointState?.()) {
			if (this.session.getLastCompletedRewind?.()) {
				throw new ToolError(
					"Checkpoint already completed; continue from the retained rewind report instead of calling rewind again.",
				);
			}
			throw new ToolError("No active checkpoint. Create a checkpoint before calling rewind.");
		}
		const report = params.report.trim();
		if (report.length === 0) {
			throw new ToolError("Report cannot be empty.");
		}
		return toolResult<RewindToolDetails>({ report, rewound: true })
			.text(["Rewind requested.", "Report captured for context replacement."].join("\n"))
			.done();
	}
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Do not call the rewind tool again; read the report from session.getLastCompletedRewind() and continue from it.
  2. If a real second rewind is needed, create a new checkpoint first, then call rewind.
  3. Clear the completed-rewind marker (or start a fresh session) before issuing a new rewind request.

Example fix

// before (agent tool loop)
await session.tools.rewind({ report: secondReport });

// after
const previous = session.getLastCompletedRewind?.();
if (previous && !session.getCheckpointState?.()) {
  usePreviousReport(previous.report); // don't call rewind again
} else {
  await session.tools.rewind({ report: secondReport });
}
Defensive patterns

Strategy: validation

Validate before calling

if (!session.getCheckpointState?.() && session.getLastCompletedRewind?.()) {
  // rewind already done — use the retained report, don't call rewind again
}

Try / catch

try {
  await rewindTool.execute(params, ...);
} catch (err) {
  if (err instanceof ToolError && /already completed/.test(err.message)) {
    const report = session.getLastCompletedRewind?.()?.report;
    // continue from retained report
  } else throw err;
}

Prevention

When it happens

Trigger: Calling the rewind tool's execute() when session.getCheckpointState() returns falsy AND session.getLastCompletedRewind() returns truthy — i.e. a rewind already finished earlier in the same session and the tool is invoked again.

Common situations: An LLM agent retries the rewind tool call because it didn't register that the first call succeeded; a scripted workflow calls rewind twice expecting idempotence; session state was restored mid-run losing the live checkpoint but retaining the completed-rewind marker.

Related errors


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