CherryHQ/cherry-studio · error · Error

Channel not found: ${channelId}

Error message

Channel not found: ${channelId}

What it means

Thrown by ChannelMessageHandler.createSessionForChannel when neither a passed-in channel row nor channelService.getChannel(channelId) resolves to a channel. The handler needs the channel's workspace to create an agent session, so a missing channel row is a hard stop.

Source

Thrown at src/main/ai/channels/ChannelMessageHandler.ts:773

      channelSessionId: channelRow?.sessionId ?? null,
      trackerKey
    })

    const newSession = this.createSessionForChannel(agentId, channelId, channelRow ?? undefined)
    channelService.updateChannel(channelId, { sessionId: newSession.id })
    this.sessionTracker.set(trackerKey, newSession.id)
    this.evictSessionTracker()
    return newSession
  }

  private createSessionForChannel(
    agentId: string,
    channelId: string,
    channel?: NonNullable<Awaited<ReturnType<typeof channelService.getChannel>>>
  ): AgentSessionEntity {
    const channelRow = channel ?? channelService.getChannel(channelId)
    if (!channelRow) {
      throw new Error(`Channel not found: ${channelId}`)
    }
    return agentSessionService.create({
      agentId,
      name: 'Channel session',
      workspace: channelRow.workspace
    })
  }

  private async collectStreamResponse(
    session: AgentSessionEntity,
    content: string,
    abortController: AbortController,
    adapter: ChannelAdapter,
    chatId: string,
    replyToMessageId?: string,
    onAdmitted?: () => void
  ): Promise<string> {
    if (!session.agentId) {

View on GitHub (pinned to 726446b54c)

Solutions

  1. Verify the channel exists: `channelService.getChannel(channelId)` before dispatching.
  2. If the channel was deleted, drop/ignore the inbound message and stop its adapter subscription.
  3. Ensure channel rows are created (and committed) before the adapter starts receiving for that channelId.
  4. Catch at the handler boundary and reply with / log a 'channel not configured' result instead of crashing the handler.

Example fix

// before: assume the channel exists
const session = createSessionForChannel(agentId, channelId)

// after: resolve up front and guard
const channel = channelService.getChannel(channelId)
if (!channel) { logger.warn('Inbound for unknown channel', { channelId }); return }
const session = createSessionForChannel(agentId, channelId, channel)
Defensive patterns

Strategy: type-guard

Validate before calling

const channel = channelService.getChannel(channelId)
if (!channel) {
  logger.warn('Inbound message for unknown channel', { channelId })
  return // do not attempt to create a session
}
const session = createSessionForChannel(agentId, channelId, channel)

Type guard

function channelExists(c: unknown): c is { id: string; workspace: unknown } {
  return c != null && typeof (c as { id?: unknown }).id === 'string'
}

Try / catch

try {
  const session = createSessionForChannel(agentId, channelId)
} catch (e) {
  if (e instanceof Error && /Channel not found:/.test(e.message)) {
    // drop the inbound message; stop the adapter subscription for the dead channel
    logger.warn('Dropping inbound for missing channel', { channelId })
  } else throw e
}

Prevention

When it happens

Trigger: createSessionForChannel(agentId, channelId, channel?) is called with no channel argument and channelService.getChannel(channelId) returns null — e.g. an inbound message references a channelId that was deleted or never created.

Common situations: A channel was deleted while messages were still inbound; an integration/webhook delivered a message for an unregistered channel; channelId mismatch (rename/typo); race between channel creation and first message.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/474ef9675e919d4e. Report an issue: GitHub.