can1357/oh-my-pi · error
Composer is not available for transfer
Error message
Composer is not available for transfer
What it means
Composer.transfer() moves terminal ownership from the composer to InteractiveMode without tearing the composer down. It throws when the composer is not in a state that can be transferred: it was never started, it has already been stopped, or ownership was already transferred. This is a lifecycle-state guard, not a resource failure.
Source
Thrown at packages/coding-agent/src/modes/composer.ts:574
this.ui.removeChild(this.#bootstrapInputGap);
this.ui.removeChild(this.editor);
this.#runtimeMounted = true;
}
this.#runtimeChildren = children;
for (const child of children) this.ui.addChild(child);
this.ui.addChild(this.#statusHost);
this.ui.requestRender();
}
/** Play or replay the welcome intro against the stable header render target. */
playWelcomeIntro(): void {
this.#welcome?.playIntro(() => this.ui.requestComponentRender(this.#header));
}
/** Transfer terminal ownership to InteractiveMode without stopping the composer. */
transfer(): void {
if (!this.#started || this.#stopped || this.#transferred) {
throw new Error("Composer is not available for transfer");
}
this.#transferred = true;
}
/** Stop a composer that has not transferred terminal ownership. */
stop(): void {
if (!this.#started || this.#stopped || this.#transferred) return;
this.#welcome?.stopIntro();
this.ui.stop();
this.#stopped = true;
}
#applyWelcomeUpdate(update: ComposerWelcomeUpdate): void {
if (update.version !== undefined) this.#version = update.version;
if (update.modelName !== undefined) this.#modelName = update.modelName;
if (update.providerName !== undefined) this.#providerName = update.providerName;
if (update.recentSessions !== undefined) this.#recentSessions = [...update.recentSessions];
if (update.lspServers !== undefined) this.#lspServers = [...update.lspServers];View on GitHub (pinned to 9690622007)
Solutions
- Ensure composer.start() has completed before calling transfer()
- Track transfer state in the caller and skip subsequent transfer() calls
- Use composer.stop() instead of transfer() if the intent is to shut the composer down rather than hand off the terminal
- Check the ordering of mode-switch handlers so only one path invokes transfer()
Example fix
// before
composer.transfer();
// after
if (composer.isRunning() && !composer.hasTransferred()) {
composer.transfer();
} Defensive patterns
Strategy: try-catch
Validate before calling
// Track lifecycle in caller state const canTransfer = composer.isStarted && !composer.isStopped && !composer.hasTransferred; if (!canTransfer) return;
Type guard
function isTransferable(c: Composer): boolean {
return c.isStarted && !c.isStopped && !c.hasTransferred;
} Try / catch
try {
composer.transfer();
} catch (err) {
if (err instanceof Error && err.message === 'Composer is not available for transfer') {
// already stopped/transferred or not started; skip handoff
return;
}
throw err;
} Prevention
- Call transfer() exactly once per composer lifecycle and centralize the call site
- Only transfer after start() has completed and before any stop()
- Use a state flag in the owning mode to prevent double dispatch
When it happens
Trigger: Calling composer.transfer() before composer.start() was called; calling transfer() after composer.stop() has run; calling transfer() a second time after a previous successful transfer (#transferred already true).
Common situations: Double-dispatching a mode-switch keybinding so the transfer path runs twice; a plugin or custom mode calling transfer() during early startup before the composer finished initializing; calling transfer() from a cleanup/teardown path after the session was already stopped.
Related errors
- A prepaint composer is already active
- ${operation} requires an active cmux browser run
- Unexpected response ${result.op}
- DAP adapter ${this.adapter.name} is not running
- Debug session ${root.id} is still active. Terminate it befor
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/d0b67bd0e82489f0.
Report an issue: GitHub.