{"record":{"id":"a8fcaa38b2ad28b3","repo":"chenhg5/cc-connect","slug":"tuitui-unexpected-replyctx-type-t","errorCode":null,"errorMessage":"tuitui: unexpected replyCtx type %T","messagePattern":"tuitui: unexpected replyCtx type %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/tuitui/tuitui.go","lineNumber":582,"sourceCode":"\t\treturn true\n\t}\n\treturn p.isRecentOutboundEcho(env)\n}\n\nfunc (p *Platform) sessionKey(env inboundEnvelope) string {\n\tif env.chatType == chatTypeDirect {\n\t\treturn \"tuitui:\" + env.senderID\n\t}\n\tif p.shareSessionInChannel || env.chatType == chatTypeChannel {\n\t\treturn \"tuitui:\" + env.chatID\n\t}\n\treturn \"tuitui:\" + env.chatID + \":\" + env.senderID\n}\n\nfunc requireReplyContext(replyCtx any) (replyContext, error) {\n\trctx, ok := replyCtx.(replyContext)\n\tif !ok {\n\t\treturn replyContext{}, fmt.Errorf(\"tuitui: unexpected replyCtx type %T\", replyCtx)\n\t}\n\tif rctx.chatType == \"\" {\n\t\trctx.chatType = guessChatType(rctx.chatID)\n\t}\n\treturn rctx, nil\n}\n\nfunc (p *Platform) sendText(ctx context.Context, replyCtx any, content string) error {\n\trctx, err := requireReplyContext(replyCtx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tpayload := map[string]any{\n\t\t\"msgtype\": \"text\",\n\t\t\"text\": map[string]string{\n\t\t\t\"content\": content,\n\t\t},\n\t}","sourceCodeStart":564,"sourceCodeEnd":600,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/tuitui/tuitui.go#L564-L600","documentation":"requireReplyContext asserts that the replyCtx passed to a send API is the platform's internal replyContext struct. SendImage, SendFile and sendText all require this concrete type; anything else (nil, a string chat ID, a context from a different platform) fails the assertion. It exists because Tuitui sends always need a chatID and optionally a chatType, which the struct carries.","triggerScenarios":"Calling p.SendImage, p.SendFile or p.sendText with a replyCtx argument that is not a platform/tuitui.replyContext value — e.g. nil, a raw chat-ID string, or a reply context struct from another platform package.","commonSituations":"Wiring a generic bridge that passes reply contexts between platforms; caching a replyCtx as an untyped any and storing the wrong value; calling send helpers directly in tests with hand-built arguments.","solutions":["Pass the replyContext value obtained from an inbound envelope (env-based helpers) rather than constructing one ad hoc.","If building one manually, construct platform/tuitui.replyContext{ChatID: ..., ChatType: ..., MessageID: ...} so the assertion succeeds.","Search call sites for typed-nil or wrong-type arguments passed as any and fix the caller's type.","Wrap the call site in a type check so the wrong type is rejected before reaching the platform."],"exampleFix":"// before\np.SendImage(ctx, chatIDString, img)\n// after\nrctx := tuitui.ReplyContextFromEnvelope(env)\np.SendImage(ctx, rctx, img)","handlingStrategy":"type-guard","validationCode":"if rctx, ok := replyCtx.(tuitui.ReplyContext); !ok || rctx.ChatID == \"\" {\n    return fmt.Errorf(\"caller must pass a tuitui replyContext, got %T\", replyCtx)\n}","typeGuard":"func asReplyContext(v any) (replyContext, bool) {\n    rc, ok := v.(replyContext)\n    return rc, ok && rc.chatID != \"\"\n}","tryCatchPattern":null,"preventionTips":["Never pass raw strings or foreign platform structs as replyCtx","Type replyCtx parameters as replyContext (not any) in your own wrappers","Keep a single helper that extracts replyContext from inbound envelopes"],"tags":["go","type-assertion","tuitui"],"backgroundTag":"type-mismatch","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}