{"record":{"id":"4c6b2c043e284cbd","repo":"musistudio/claude-code-router","slug":"no-bot-gateway-conversationref-is-available-for-in","errorCode":null,"errorMessage":"No Bot Gateway conversationRef is available for inbound bot response.","messagePattern":"No Bot Gateway conversationRef is available for inbound bot response\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/agents/codex/cli-middleware-runtime.ts","lineNumber":5514,"sourceCode":"    };\n    await this.sendDurable(outbound, { kind: \"handoff\", sourceKey: key });\n    this.rememberForwarded(key);\n    log(\"bot_gateway_forward_sent\", {\n      key,\n      reason: decision.reason,\n      textLen: text.length,\n      threadId: params.threadId || \"\",\n      turnId: params.turnId || \"\"\n    });\n  }\n\n  async sendReplyToEvent(event, text, key) {\n    if (!text || !String(text).trim()) return;\n    await this.ensureStarted();\n    text = localizeBotReply(String(text), botLanguageForEvent(this.config.language, event));\n    const conversationRef = conversationRefFromEvent(event) || this.config.conversationRef;\n    if (!conversationRef) {\n      throw new Error(\"No Bot Gateway conversationRef is available for inbound bot response.\");\n    }\n    const chunks = splitBotMessage(String(text), this.config.messageChunkChars);\n    for (let index = 0; index < chunks.length; index += 1) {\n      const chunk = chunks[index];\n      const outbound = this.outboundForEvent(event, conversationRef, botTextIntent(chunk), key + \":part:\" + (index + 1));\n      await this.sendDurable(outbound, { kind: \"reply\", sourceKey: key });\n    }\n  }\n\n  async sendCardToEvent(event, card, fallbackText, key) {\n    await this.ensureStarted();\n    const conversationRef = conversationRefFromEvent(event) || this.config.conversationRef;\n    if (!conversationRef) throw new Error(\"No Bot Gateway conversationRef is available for card response.\");\n    const language = botLanguageForEvent(this.config.language, event);\n    const localizedFallback = localizeBotReply(fallbackText, language);\n    const localizedCard = language === \"zh-CN\" ? localizeBotCard(card) : card;\n    const outbound = this.outboundForEvent(event, conversationRef, { type: \"card\", card: localizedCard, fallbackText: localizedFallback }, key);\n    await this.sendDurable(outbound, { kind: \"card\", sourceKey: key });","sourceCodeStart":5496,"sourceCodeEnd":5532,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/agents/codex/cli-middleware-runtime.ts#L5496-L5532","documentation":"sendReplyToEvent() must route a reply back to the conversation an inbound bot event came from. It derives conversationRef from the event (conversationRefFromEvent) with the configured value as fallback; if both are empty it throws because there is nowhere to deliver the reply.","triggerScenarios":"Replying to a bot event that carries no conversation reference (malformed/webhook-replayed event, trimmed payload) while no config.conversationRef fallback exists.","commonSituations":"Webhook gateway forwarding events with the conversation field stripped; event objects constructed manually in tests without a conversationRef; schema change in the gateway payload renaming the conversation field; empty fallback config.","solutions":["Set config.conversationRef as a fallback so replies always have a destination","Inspect the inbound event payload and fix the gateway/forwarder that drops the conversation field","Update to a version matching the gateway's current event schema if the field was renamed","Skip non-conversational events early instead of attempting a reply"],"exampleFix":"// before\nawait gateway.sendReplyToEvent(event, text, key); // throws when event has no ref\n\n// after\nconst ref = conversationRefFromEvent(event) || gateway.config.conversationRef;\nif (ref) {\n  await gateway.sendReplyToEvent(event, text, key);\n} else {\n  logger.warn(\"dropping reply: no conversationRef on event\", event);\n}","handlingStrategy":"validation","validationCode":"const ref = conversationRefFromEvent(event) || gateway.config.conversationRef; if (!ref) { /* drop or alert instead of replying */ }","typeGuard":"function eventHasConversationRef(event) { return Boolean(conversationRefFromEvent(event)); }","tryCatchPattern":"try { await gateway.sendReplyToEvent(event, text, key); } catch (e) { if (/no conversationRef is available for inbound/i.test(String(e))) { log.warn(\"dropping reply\", { event }); } else throw e; }","preventionTips":["Set config.conversationRef as a universal fallback","Validate gateway-forwarded events retain conversation fields","Skip non-conversational events before attempting replies"],"tags":["bot-gateway","reply","conversation-ref","event-payload"],"backgroundTag":"missing-destination-address","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}