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

shutdown_${scope}_${kind.replaceAll(' ', '_')}_identity_chan

Error message

shutdown_${scope}_${kind.replaceAll(' ', '_')}_identity_changed:${authorization.paneId}

What it means

Thrown by assertSharedPaneAuthorizationContinuous when, mid-shutdown, a pane's live proof is gone or its PID changed relative to the frozen authorization. It guards against the pane being swapped between authorization and the destructive teardown step.

Source

Thrown at src/team/runtime.ts:5205

        }
      }
      const owner = readPaneTeamOwnerTagResult(paneId);
      if (owner.status === 'error') {
        throw new Error(`shutdown_shared_session_${kind.replaceAll(' ', '_')}_owner_unavailable:${paneId}:${owner.error}`);
      }
      if (owner.status !== 'value' || !tmuxPaneOwnerId || owner.value !== tmuxPaneOwnerId) {
        throw new Error(`shutdown_shared_session_${kind.replaceAll(' ', '_')}_owner_changed:${paneId}`);
      }
      return { paneId, pid: proof.pid, owner: owner.value };
    };
    const assertSharedPaneAuthorizationContinuous = (
      authorization: FrozenSharedPaneAuthorization,
      kind: 'HUD pane' | 'restore leader pane',
    ): void => {
      const scope = sharedSessionTopology ? 'shared_session' : 'detached_session';
      const proof = readExactPaneProofSync(authorization.paneId);
      if (proof.status !== 'live' || proof.pid !== authorization.pid) {
        throw new Error(`shutdown_${scope}_${kind.replaceAll(' ', '_')}_identity_changed:${authorization.paneId}`);
      }
      const owner = readPaneTeamOwnerTagResult(authorization.paneId);
      if (owner.status === 'error') {
        throw new Error(`shutdown_${scope}_${kind.replaceAll(' ', '_')}_owner_unavailable:${authorization.paneId}:${owner.error}`);
      }
      const currentOwner = owner.status === 'value' ? owner.value : null;
      if (currentOwner !== authorization.owner) {
        throw new Error(`shutdown_${scope}_${kind.replaceAll(' ', '_')}_owner_changed:${authorization.paneId}`);
      }
    };
    const persistedHudPaneId = typeof hudPaneId === 'string' && /^%[0-9]+$/.test(hudPaneId)
      ? hudPaneId
      : null;
    const persistedLeaderPaneId = typeof leaderPaneId === 'string' && /^%[0-9]+$/.test(leaderPaneId)
      ? leaderPaneId
      : null;
    if (persistedHudPaneId && effectiveHudPaneId === persistedHudPaneId && (typeof hudPanePid !== 'number' || !Number.isSafeInteger(hudPanePid) || hudPanePid <= 0)) {
      const scope = sharedSessionTopology ? 'shared_session' : 'detached_session';

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Avoid touching the shared session (manual kills, respawn hooks) while shutdown is running
  2. Disable automatic respawn (remain-on-exit off, respawn-pane hooks) during teardown, or make them idempotent
  3. Retry the shutdown operation — if the pane died naturally, the next run should skip it
Defensive patterns

Strategy: retry

Try / catch

catch (e) { if (String(e.message).includes('_identity_changed:')) { await sleep(500); return shutdown(retries + 1); } throw e; }

Prevention

When it happens

Trigger: After freezeSharedPaneAuthorization captured {paneId,pid}, readExactPaneProofSync returns non-live or a different pid before/after teardown steps — pane exited or was respawned during shutdown.

Common situations: Long-running teardown steps where tmux respawns panes (remain-on-exit + respawn hooks); user manually closing panes during shutdown; flaky proof file persistence.

Related errors


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