{"record":{"id":"02596255270a577e","repo":"Tencent/WeKnora","slug":"channel-not-found-s","errorCode":null,"errorMessage":"channel not found: %s","messagePattern":"channel not found: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/im/service.go","lineNumber":1693,"sourceCode":"\t\t\tlogger.Infof(ctx, \"[IM] Skipping duplicate message: %s\", msg.MessageID)\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t// Reject overly long messages to protect the QA pipeline\n\tcontentRunes := []rune(msg.Content)\n\tif len(contentRunes) > maxContentLength {\n\t\tlogger.Warnf(ctx, \"[IM] Message too long (%d runes), truncating to %d\", len(contentRunes), maxContentLength)\n\t\tmsg.Content = string(contentRunes[:maxContentLength])\n\t}\n\n\t// Get channel config (moved before rate limit so we can reply to the user)\n\tadapter, channel, ok := s.GetChannelAdapter(channelID)\n\tif !ok {\n\t\t// Try loading from DB (channel might have been created after service start)\n\t\tch, err := s.GetChannelByID(channelID)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"channel not found: %s\", channelID)\n\t\t}\n\t\t// Start it dynamically\n\t\tif err := s.StartChannel(ch); err != nil {\n\t\t\treturn fmt.Errorf(\"start channel %s: %w\", channelID, err)\n\t\t}\n\t\tadapter, channel, ok = s.GetChannelAdapter(channelID)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"channel adapter not available after start: %s\", channelID)\n\t\t}\n\t}\n\n\t// Resolve threadID for key building — only include in thread mode to avoid\n\t// leaking thread scope into user-mode rate limit / inflight keys.\n\tthreadID := \"\"\n\tif channel.SessionMode == string(SessionModeThread) {\n\t\tthreadID = msg.ThreadID\n\t}\n","sourceCodeStart":1675,"sourceCodeEnd":1711,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/im/service.go#L1675-L1711","documentation":"When handling an incoming message, the service first tries GetChannelAdapter(channelID); if the channel isn't loaded it attempts to load it from the DB and start it dynamically. If the DB lookup also fails (channel truly doesn't exist), this error is returned.","triggerScenarios":"A webhook/event arrives for a channelID that is neither running nor present in the database — e.g. the channel was deleted, or the ID in the platform URL is wrong.","commonSituations":"Stale webhooks pointing at a deleted channel; recreating a channel which changed its ID; environment mismatch (webhook registered against a different DB/stage); typo in the channel ID portion of a callback URL.","solutions":["Verify the channel ID exists in the DB and re-create it if deleted","Re-register the webhook/subscription with the correct channel ID","Check you're pointing at the right environment/database (prod vs staging)"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"if _, err := svc.GetChannelByID(channelID); err != nil {\n    log.Printf(\"unknown channel %s; check webhook registration\", channelID)\n    return\n}","typeGuard":null,"tryCatchPattern":"adapter, channel, ok := svc.GetChannelAdapter(channelID)\nif !ok {\n    if _, err := svc.GetChannelByID(channelID); err != nil {\n        // channel truly missing: log and drop the event instead of crashing\n        return\n    }\n}","preventionTips":["Re-register webhooks whenever channels are recreated or IDs change","Periodically reconcile platform webhook subscriptions against the channels table","Tag events with the environment so staging webhooks don't hit prod DBs"],"tags":["channel","not-found","lookup"],"backgroundTag":"resource-not-found","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}