musistudio/claude-code-router · error

No Bot Gateway conversationRef is available for media respon

Error message

No Bot Gateway conversationRef is available for media response.

What it means

Thrown by sendMediaToEvent when the middleware cannot resolve a Bot Gateway conversationRef for the event. The code first tries conversationRefFromEvent(event) and then falls back to this.config.conversationRef; if both are undefined there is no routing address to deliver the media payload to, so it refuses to send.

Source

Thrown at packages/core/src/agents/codex/cli-middleware-runtime.ts:5539

    }
  }

  async sendCardToEvent(event, card, fallbackText, key) {
    await this.ensureStarted();
    const conversationRef = conversationRefFromEvent(event) || this.config.conversationRef;
    if (!conversationRef) throw new Error("No Bot Gateway conversationRef is available for card response.");
    const language = botLanguageForEvent(this.config.language, event);
    const localizedFallback = localizeBotReply(fallbackText, language);
    const localizedCard = language === "zh-CN" ? localizeBotCard(card) : card;
    const outbound = this.outboundForEvent(event, conversationRef, { type: "card", card: localizedCard, fallbackText: localizedFallback }, key);
    await this.sendDurable(outbound, { kind: "card", sourceKey: key });
  }

  async sendMediaToEvent(event, media, caption, key) {
    if (!this.config.mediaEnabled) return;
    await this.ensureStarted();
    const conversationRef = conversationRefFromEvent(event) || this.config.conversationRef;
    if (!conversationRef) throw new Error("No Bot Gateway conversationRef is available for media response.");
    const fallbackText = caption || media.filename || media.url || "Attachment";
    const outbound = this.outboundForEvent(event, conversationRef, { type: "media", media, caption, fallbackText }, key);
    await this.sendDurable(outbound, { kind: "media", sourceKey: key });
  }

  async sendStreamToEvent(event, streamId, text, final, key) {
    if (!this.config.streamReplies || !text) return;
    await this.ensureStarted();
    const conversationRef = conversationRefFromEvent(event) || this.config.conversationRef;
    if (!conversationRef) return;
    const outbound = this.outboundForEvent(event, conversationRef, {
      type: "stream_text",
      streamId,
      text,
      final: Boolean(final),
      fallbackText: text
    }, key + ":" + (final ? "final" : stableBotKey(text.slice(-160))));
    await this.sendDurable(outbound, { kind: "stream", sourceKey: key });

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Pass a conversationRef in the middleware config: new CodexCliMiddlewareRuntime({ conversationRef, ... }) so a fallback always exists
  2. Ensure the event object actually contains the Bot Gateway conversationRef payload before calling sendMediaToEvent
  3. If the event is optional, guard with conversationRefFromEvent(event) ?? config.conversationRef before calling and skip sending when absent

Example fix

// before
await runtime.sendMediaToEvent(event, media, caption);
// after
if (conversationRefFromEvent(event) || runtime.config.conversationRef) {
  await runtime.sendMediaToEvent(event, media, caption);
} else {
  // log and skip media delivery
}
Defensive patterns

Strategy: validation

Validate before calling

const ref = conversationRefFromEvent(event) ?? runtime.config.conversationRef;
if (!ref) {
  // skip media delivery, log context
  return;
}
await runtime.sendMediaToEvent(event, media, caption, key);

Type guard

function hasConversationRef(event: unknown): boolean {
  return conversationRefFromEvent(event as never) != null;
}

Try / catch

try { await runtime.sendMediaToEvent(event, media, caption); } catch (e) { if (e instanceof Error && e.message.includes('conversationRef')) { /* degrade gracefully */ } else throw e; }

Prevention

When it happens

Trigger: Calling sendMediaToEvent(event, media, ...) with mediaEnabled=true on an event that carries no conversation reference (e.g. a synthetic or webhook event without a conversationRef field) while the runtime was started without config.conversationRef, or before conversationRef was populated.

Common situations: Testing with mock/seeded events that omit conversationRef; misconfigured middleware where the default conversationRef option was not passed at construction; processing events from a different channel type that doesn't embed a conversationRef.

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/d66ac61eee61f911. Report an issue: GitHub.