{"record":{"id":"058c69a0ce6b0f3b","repo":"can1357/oh-my-pi","slug":"compaction-already-in-progress","errorCode":null,"errorMessage":"Compaction already in progress","messagePattern":"Compaction already in progress","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/coding-agent/src/session/session-maintenance.ts","lineNumber":718,"sourceCode":"\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Manually compact the session context.\n\t * Aborts current agent operation first.\n\t * @param customInstructions Optional instructions for the compaction summary\n\t * @param options Optional callbacks for completion/error handling\n\t */\n\tasync compact(\n\t\tcustomInstructions?: string,\n\t\toptions?: CompactOptions,\n\t\tmethodOffset = 0,\n\t\tretryController?: AbortController,\n\t): Promise<CompactionResult> {\n\t\tconst ownsCompactionController = retryController === undefined;\n\t\tif (this.#compactionAbortController && this.#compactionAbortController !== retryController) {\n\t\t\tthrow new Error(\"Compaction already in progress\");\n\t\t}\n\t\t// Resolve the `/compact <mode>` subcommand up front so input validation\n\t\t// runs before we disconnect/abort the active agent operation below.\n\t\tconst compactMode = options?.mode ? findCompactMode(options.mode) : undefined;\n\t\t// Modes that produce no LLM summary (snapcompact) have nothing to focus.\n\t\t// Reject focus text loudly so programmatic callers don't silently lose\n\t\t// instructions (the slash path pre-validates via parseCompactArgs).\n\t\t// `internalGuidance` counts the same way — plan-mode approval never\n\t\t// combines with a rejects-focus mode, but reject early if a caller ever\n\t\t// wires it up so we don't silently drop the directive on the snapcompact\n\t\t// fallback (issue #4359).\n\t\tif (compactMode?.rejectsFocus && (customInstructions || options?.internalGuidance)) {\n\t\t\tthrow new Error(`/compact ${compactMode.name} does not take focus instructions.`);\n\t\t}\n\t\tlet methods: CompactionMethod[] = [];\n\t\tlet selectedMethodIndex = -1;\n\t\tlet compactionCommitted = false;\n\t\tlet methodAttempted = false;","sourceCodeStart":700,"sourceCodeEnd":736,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/session/session-maintenance.ts#L700-L736","documentation":"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.","triggerScenarios":"Calling compact (or a compaction method) while this.#compactionAbortController is set and different from the provided retryController — i.e. a second concurrent compaction attempt.","commonSituations":"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.","solutions":["Wait for the in-flight compaction to finish before starting another (await its promise or listen for completion)","Skip the new request if compaction is already active (query maintenance state first)","Queue or coalesce compaction requests instead of issuing them concurrently","If stale state persists with no compaction actually running, reset the session/maintenance state (bug) and file an issue"],"exampleFix":"// before\nawait session.compact(); // throws if one is running\n// after\nif (!maintenance.isCompacting()) {\n  await session.compact();\n} else {\n  await maintenance.waitForCompaction();\n}","handlingStrategy":"validation","validationCode":"if (maintenance.isCompacting?.()) {\n  return; // or await the in-flight promise\n}\nawait session.compact(options);","typeGuard":null,"tryCatchPattern":"try {\n  await session.compact(options);\n} catch (err) {\n  if (err instanceof Error && err.message === \"Compaction already in progress\") {\n    await existingCompactionPromise; // coalesce instead of failing\n  } else throw err;\n}","preventionTips":["Guard against concurrent compaction calls with a session-level flag","Coalesce or queue compaction requests rather than issuing in parallel","Disable manual /compact while auto-compaction runs","Ensure error paths always clear the compaction controller so state never goes stale"],"tags":["concurrency","compaction","state","reentrancy"],"backgroundTag":"operation-already-in-progress","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}