{"record":{"id":"2809566eb1cd9e94","repo":"chenhg5/cc-connect","slug":"tuitui-markdown-is-required","errorCode":null,"errorMessage":"tuitui: markdown is required","messagePattern":"tuitui: markdown is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/tuitui/tuitui.go","lineNumber":206,"sourceCode":"\t}\n\treturn nil\n}\n\nfunc (p *Platform) Reply(ctx context.Context, replyCtx any, content string) error {\n\treturn p.sendText(ctx, replyCtx, content)\n}\n\nfunc (p *Platform) Send(ctx context.Context, replyCtx any, content string) error {\n\treturn p.sendText(ctx, replyCtx, content)\n}\n\nfunc (p *Platform) SendChannelPost(ctx context.Context, channelID, markdown, parentID string) error {\n\tchannelID = strings.TrimSpace(channelID)\n\tif channelID == \"\" {\n\t\treturn fmt.Errorf(\"tuitui: channel id is required\")\n\t}\n\tif strings.TrimSpace(markdown) == \"\" {\n\t\treturn fmt.Errorf(\"tuitui: markdown is required\")\n\t}\n\n\tchatID := channelID\n\tif guessChatType(channelID) == chatTypeChannel {\n\t\tif parentID = strings.TrimSpace(parentID); parentID != \"\" {\n\t\t\ttarget := teamsParseChatID(channelID)\n\t\t\tchatID = teamsBuildChatID(target[\"team_id\"], target[\"channel_id\"], parentID)\n\t\t}\n\t} else {\n\t\tinfo, err := p.getChannelInfo(ctx, channelID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tteamID := stringFromAny(info[\"team_id\"])\n\t\tif teamID == \"\" {\n\t\t\treturn fmt.Errorf(\"tuitui: team_id not found for channel %q\", channelID)\n\t\t}\n\t\tchatID = teamsBuildChatID(teamID, channelID, strings.TrimSpace(parentID))","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/tuitui/tuitui.go#L188-L224","documentation":"SendChannelPost validates that the markdown body is non-empty after trimming. A post with no content cannot be rendered by TuiTui, so the call is rejected before building the payload. It runs right after the channel ID check.","triggerScenarios":"Calling SendChannelPost(ctx, channelID, \"\", parentID) or with whitespace-only markdown (e.g. \"\\n  \").","commonSituations":"An agent produced empty output and the caller forwarded it verbatim; a template render that failed silently and yielded an empty string; stripping formatting removed all content.","solutions":["Ensure the markdown content is non-empty before calling; build a placeholder or error message if the body is empty.","Check the upstream producer (agent output, template render) for why the content is blank.","Skip the SendChannelPost call entirely when there is nothing to send."],"exampleFix":"// before\np.SendChannelPost(ctx, chatID, renderTemplate(t), \"\") // renders to \"\"\n// after\nmd := renderTemplate(t)\nif strings.TrimSpace(md) == \"\" {\n    md = \"_(no content)_\"\n}\np.SendChannelPost(ctx, chatID, md, \"\")","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(markdown) == \"\" {\n    return fmt.Errorf(\"cannot post: empty markdown\")\n}","typeGuard":"func hasContent(s string) bool { return strings.TrimSpace(s) != \"\" }","tryCatchPattern":"if err := p.SendChannelPost(ctx, chID, md, parent); err != nil && strings.Contains(err.Error(), \"markdown is required\") {\n    log.Warn(\"skipping empty post\")\n}","preventionTips":["Check rendered templates for empty output before sending.","Guard agent output pipelines with a non-empty content assertion.","Skip no-op sends instead of calling the API with blank bodies."],"tags":["validation","argument","markdown"],"backgroundTag":"missing-required-argument","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"}