can1357/oh-my-pi · warning · Error

Recipient session is disposed.

Error message

Recipient session is disposed.

What it means

IrcBridge.deliver() injects an IRC message into the recipient agent session. If the host session has already been disposed, delivery is impossible, so it throws synchronously before building the CustomMessage record. Callers should treat a disposed bridge as terminal and stop routing messages to it.

Source

Thrown at packages/coding-agent/src/session/irc-bridge.ts:120

				}
				messages.push({
					id,
					from,
					to: agentId,
					body,
					ts: record.timestamp,
					...(typeof replyTo === "string" ? { replyTo } : {}),
				});
			}
		}
		this.#interrupts = remainingInterrupts;
		this.#asides = remainingAsides;
		return messages;
	}

	/** Delivers an IRC message into the recipient session without awaiting any wake turn. */
	async deliver(msg: IrcMessage, opts?: { expectsReply?: boolean }): Promise<"injected" | "woken"> {
		if (this.#host.isDisposed()) throw new Error("Recipient session is disposed.");
		const streaming = this.#host.isStreaming();
		const planModeIdle = !streaming && this.#host.planModeEnabled();
		const autoReply =
			(opts?.expectsReply ?? false) && ((streaming && !this.#host.settings.get("async.enabled")) || planModeIdle);
		const record: CustomMessage = {
			role: "custom",
			customType: "irc:incoming",
			content: prompt.render(ircIncomingTemplate, {
				from: msg.from,
				message: msg.body,
				replyTo: msg.replyTo ?? "",
				autoReplied: autoReply,
				interrupting: streaming,
			}),
			display: true,
			details: { id: msg.id, from: msg.from, message: msg.body, ...(msg.replyTo ? { replyTo: msg.replyTo } : {}) },
			attribution: "agent",
			timestamp: msg.ts,

View on GitHub (pinned to 9690622007)

Solutions

  1. Check host.isDisposed() (or a bridge-alive flag) before calling deliver and skip/queue the message
  2. Catch this error in the IRC event handler, log it via logger.debug, and detach the bridge from the channel
  3. Unsubscribe the IRC listener as the first step of session disposal to close the race window
  4. If messages must survive, buffer them and replay into a replacement session instead of delivering to the disposed one

Example fix

// before
ircClient.on('message', async m => { await bridge.deliver(m, { expectsReply: true }); });
// after
ircClient.on('message', async m => {
  try {
    if (session.isDisposed()) return; // or re-route
    await bridge.deliver(m, { expectsReply: true });
  } catch (e) {
    if (!(e instanceof Error && e.message.startsWith('Recipient session is disposed'))) throw e;
  }
});
Defensive patterns

Strategy: validation

Validate before calling

// before deliver:
// if (bridge-host session isDisposed) skip or re-route the IRC message

Try / catch

try {
  await bridge.deliver(msg, { expectsReply: true });
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Recipient session is disposed')) {
    logger.debug('dropped IRC message for disposed session', { from: msg.nick });
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling `ircBridge.deliver(msg, opts)` after the host session was disposed — typically an IRC server event arriving during/after shutdown, or a reply path firing for a session the user already closed.

Common situations: IRC network still delivering messages while the app is quitting; a user closes one session in a multi-session setup but the IRC channel keeps routing to it; race between dispose() and an in-flight PRIVMSG handler.

Related errors


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