{"record":{"id":"ebe50613dc0229b1","repo":"chenhg5/cc-connect","slug":"s-invalid-reply-context-type-t","errorCode":null,"errorMessage":"%s: invalid reply context type %T","messagePattern":"(.+?): invalid reply context type %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/feishu/card.go","lineNumber":24,"sourceCode":"\t\"fmt\"\n\t\"log/slog\"\n\t\"strings\"\n\n\t\"github.com/chenhg5/cc-connect/core\"\n\tlark \"github.com/larksuite/oapi-sdk-go/v3\"\n\tlarkcore \"github.com/larksuite/oapi-sdk-go/v3/core\"\n\tlarkim \"github.com/larksuite/oapi-sdk-go/v3/service/im/v1\"\n)\n\nfunc plainText(content string) map[string]any {\n\treturn map[string]any{\"tag\": \"plain_text\", \"content\": content}\n}\n\n// ReplyCard sends a structured card as a reply to the original message.\nfunc (p *interactivePlatform) ReplyCard(ctx context.Context, rctx any, card *core.Card) error {\n\trc, ok := rctx.(replyContext)\n\tif !ok {\n\t\treturn fmt.Errorf(\"%s: invalid reply context type %T\", p.tag(), rctx)\n\t}\n\n\tcardJSON := renderCard(card, rc.sessionKey)\n\tif !p.shouldUseThreadOrReplyAPI(rc) {\n\t\tif rc.chatID == \"\" {\n\t\t\treturn fmt.Errorf(\"%s: chatID is empty, cannot send card\", p.tag())\n\t\t}\n\t\treturn p.createMessage(ctx, rc.chatID, larkim.MsgTypeInteractive, cardJSON, \"send card\")\n\t}\n\treturn p.replyMessage(ctx, rc, larkim.MsgTypeInteractive, cardJSON)\n}\n\n// SendCard sends a structured card as a new message to the chat.\nfunc (p *interactivePlatform) SendCard(ctx context.Context, rctx any, card *core.Card) error {\n\trc, ok := rctx.(replyContext)\n\tif !ok {\n\t\treturn fmt.Errorf(\"%s: invalid reply context type %T\", p.tag(), rctx)\n\t}","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/feishu/card.go#L6-L42","documentation":"feishu: invalid reply context type is raised in ReplyCard when the reply context value passed in cannot be asserted to the internal replyContext struct. The feishu adapter requires the opaque reply context produced by its own message-receive path. Passing any other type breaks the adapter's contract.","triggerScenarios":"ReplyCard (card.go:24) called with a reply context object that is not the feishu replyContext type — e.g. a context from a different platform or a caller-constructed value.","commonSituations":"Cross-platform handle mixing in multi-platform deployments; a caller caching reply contexts across engine restarts; writing a custom engine integration that constructs its own reply context.","solutions":["Only pass reply contexts obtained from the feishu platform's message events","Don't serialize/reconstruct reply contexts manually across restarts","Check the platform name to ensure the card is sent via the matching adapter"],"exampleFix":"// before\nerr := feishuPlatform.ReplyCard(ctx, genericCtx, card) // wrong type\n// after\nreplyCtx := msg.ReplyContext() // from the feishu event\nerr := feishuPlatform.ReplyCard(ctx, replyCtx, card)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"rc, ok := rctx.(replyContext); if !ok { return fmt.Errorf(\"feishu: invalid reply context type %T\", rctx) }","tryCatchPattern":"if err := p.ReplyCard(ctx, rctx, card); err != nil && strings.Contains(err.Error(), \"invalid reply context\") {\n    log.Error(\"reply context from wrong platform\", \"type\", fmt.Sprintf(\"%T\", rctx))\n}","preventionTips":["Route reply contexts only to the platform that produced them","Tag reply contexts with the platform name before dispatch","Rebuild contexts from live events instead of caching across restarts"],"tags":["feishu","type-assertion","internal-contract"],"backgroundTag":"type-mismatch","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}