Yeachan-Heo/oh-my-codex · critical · Error

bound setup finalization denied: ${finalization.cleanup.comp

Error message

bound setup finalization denied: ${finalization.cleanup.comparison?.reason ?? "unknown reason"}

What it means

When bound setup fails, the leader attempts to finalize/close the launch binding once; if finalization is denied (binding state comparison mismatch), this wrapped error is thrown instead of the underlying failure.

Source

Thrown at src/cli/index.ts:7065

  const nonce = payload.readyPath?.split(".").at(-2) || payload.sessionId;
  const contextKey = process.env[OMX_MADMAX_DETACHED_CONTEXT_ENV]?.trim();
  const activeRecordPath = contextKey
    ? madmaxDetachedActiveRecordPath(resolveMadmaxRunsRoot(process.env), contextKey)
    : join(omxRoot(payload.cwd), "state", "detached-active-record.json");
  let externalInterrupt: NodeJS.Signals | undefined;

  try {
    const completion = await completePreLaunchSetup(
      payload.cwd,
      payload.sessionId,
      payload.preLaunchOptions.notifyTempContract,
      payload.codexHomeOverride,
      payload.preLaunchOptions.enableNotifyFallbackAuthority,
      payload.preLaunchOptions.worktreeDirty,
    );
    if (completion.kind === "failure") {
      const finalization = await finalizeBoundOnce(binding, "setup-failure", payload.cwd);
      if (!finalization.finalized) throw new Error(`bound setup finalization denied: ${finalization.cleanup.comparison?.reason ?? "unknown reason"}`);
      throw completion.error;
    }
    const record: MadmaxDetachedActiveRecord = {
      version: 1, context_key: contextKey ?? payload.sessionId!, created_at: new Date().toISOString(),
      source_cwd: payload.cwd, argv: [payload.codexCmd], run_dir: process.env.OMX_ROOT ?? payload.cwd,
      tmux_session_name: payload.sessionName, session_id: payload.sessionId, tmux_pane_id: pane,
      launch_nonce: nonce, leader_pid: process.pid, base_state_root: binding.context.baseStateDir,
      lifecycle_phase: "ready",
    };
    const runtimeBinding = queryMadmaxDetachedRuntimeBinding(record);
    ownedRecord = writeMadmaxDetachedActiveRecord(activeRecordPath, {
      ...record,
      ...(runtimeBinding ? {
        tmux_internal_session_id: runtimeBinding.internalSessionId,
        tmux_session_created: runtimeBinding.sessionCreated,
        tmux_pane_pid: runtimeBinding.panePid,
      } : {}),
    });

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Inspect the binding file/state to see why the comparison failed (the reason is embedded in the message)
  2. Remove the stale binding / active record for that session and retry the launch
  3. Ensure only one parent CLI orchestrates a given session at a time
Defensive patterns

Strategy: try-catch

Try / catch

try { await runBoundSetup(...); } catch (e) {
  if ((e as Error).message.startsWith('bound setup finalization denied')) {
    // binding state conflicted: inspect/clear the binding, then re-launch
  } else throw e;
}

Prevention

When it happens

Trigger: completeBoundSetup returns kind 'failure' AND finalizeBoundOnce(binding, 'setup-failure', cwd) reports finalized=false, i.e. the binding's expected state no longer matches (already finalized, taken over, or mutated by another process).

Common situations: Two CLI processes racing over the same session binding, a leftover binding file from a previous crashed run, or clock/state version skew after an upgrade.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/e1740ce8d2a91453. Report an issue: GitHub.