can1357/oh-my-pi · warning · Error

Compaction already in progress

Error message

Compaction already in progress

What it means

SessionMaintenance serializes compactions with a single #compactionAbortController. When a new compaction request starts while another is already running (and the caller did not pass the existing retryController), it throws 'Compaction already in progress'. Only one compaction may run per session at a time.

Source

Thrown at packages/coding-agent/src/session/session-maintenance.ts:718

			return undefined;
		}
	}

	/**
	 * Manually compact the session context.
	 * Aborts current agent operation first.
	 * @param customInstructions Optional instructions for the compaction summary
	 * @param options Optional callbacks for completion/error handling
	 */
	async compact(
		customInstructions?: string,
		options?: CompactOptions,
		methodOffset = 0,
		retryController?: AbortController,
	): Promise<CompactionResult> {
		const ownsCompactionController = retryController === undefined;
		if (this.#compactionAbortController && this.#compactionAbortController !== retryController) {
			throw new Error("Compaction already in progress");
		}
		// Resolve the `/compact <mode>` subcommand up front so input validation
		// runs before we disconnect/abort the active agent operation below.
		const compactMode = options?.mode ? findCompactMode(options.mode) : undefined;
		// Modes that produce no LLM summary (snapcompact) have nothing to focus.
		// Reject focus text loudly so programmatic callers don't silently lose
		// instructions (the slash path pre-validates via parseCompactArgs).
		// `internalGuidance` counts the same way — plan-mode approval never
		// combines with a rejects-focus mode, but reject early if a caller ever
		// wires it up so we don't silently drop the directive on the snapcompact
		// fallback (issue #4359).
		if (compactMode?.rejectsFocus && (customInstructions || options?.internalGuidance)) {
			throw new Error(`/compact ${compactMode.name} does not take focus instructions.`);
		}
		let methods: CompactionMethod[] = [];
		let selectedMethodIndex = -1;
		let compactionCommitted = false;
		let methodAttempted = false;

View on GitHub (pinned to 9690622007)

Solutions

  1. Wait for the in-flight compaction to finish before starting another (await its promise or listen for completion)
  2. Skip the new request if compaction is already active (query maintenance state first)
  3. Queue or coalesce compaction requests instead of issuing them concurrently
  4. If stale state persists with no compaction actually running, reset the session/maintenance state (bug) and file an issue

Example fix

// before
await session.compact(); // throws if one is running
// after
if (!maintenance.isCompacting()) {
  await session.compact();
} else {
  await maintenance.waitForCompaction();
}
Defensive patterns

Strategy: validation

Validate before calling

if (maintenance.isCompacting?.()) {
  return; // or await the in-flight promise
}
await session.compact(options);

Try / catch

try {
  await session.compact(options);
} catch (err) {
  if (err instanceof Error && err.message === "Compaction already in progress") {
    await existingCompactionPromise; // coalesce instead of failing
  } else throw err;
}

Prevention

When it happens

Trigger: Calling compact (or a compaction method) while this.#compactionAbortController is set and different from the provided retryController — i.e. a second concurrent compaction attempt.

Common situations: User runs /compact twice in quick succession; automatic context-window compaction triggers while a manual compaction is running; programmatic compaction racing a UI-initiated one; a previous compaction left the controller uncleared after a crash path.

Related errors


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