{"record":{"id":"397dd92c59f5a305","repo":"chenhg5/cc-connect","slug":"dingtalk-proactive-send-requires-conversationid","errorCode":null,"errorMessage":"dingtalk: proactive send requires conversationId (group) or senderStaffId (direct)","messagePattern":"dingtalk: proactive send requires conversationId \\(group\\) or senderStaffId \\(direct\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/dingtalk/dingtalk.go","lineNumber":1675,"sourceCode":"\t\tmsgParam, _ := json.Marshal(map[string]string{\"text\": content})\n\t\trequestBody = map[string]any{\n\t\t\t\"robotCode\":          p.robotCode,\n\t\t\t\"openConversationId\": rc.conversationId,\n\t\t\t\"msgKey\":             \"sampleMarkdown\",\n\t\t\t\"msgParam\":           string(msgParam),\n\t\t}\n\t} else if rc.senderStaffId != \"\" {\n\t\t// Direct message via /v1.0/robot/oToMessages/batchSend\n\t\tapiURL = \"https://api.dingtalk.com/v1.0/robot/oToMessages/batchSend\"\n\t\tmsgParam, _ := json.Marshal(map[string]string{\"title\": cardTitleFromContent(content), \"text\": content})\n\t\trequestBody = map[string]any{\n\t\t\t\"robotCode\": p.robotCode,\n\t\t\t\"userIds\":   []string{rc.senderStaffId},\n\t\t\t\"msgKey\":    \"sampleMarkdown\",\n\t\t\t\"msgParam\":  string(msgParam),\n\t\t}\n\t} else {\n\t\treturn fmt.Errorf(\"dingtalk: proactive send requires conversationId (group) or senderStaffId (direct)\")\n\t}\n\n\tbody, err := json.Marshal(requestBody)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"dingtalk: marshal proactive message: %w\", err)\n\t}\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, bytes.NewReader(body))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"dingtalk: create proactive request: %w\", err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\treq.Header.Set(\"x-acs-dingtalk-access-token\", token)\n\n\tresp, err := p.httpClient.Do(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"dingtalk: proactive send request: %w\", err)\n\t}","sourceCodeStart":1657,"sourceCodeEnd":1693,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/dingtalk/dingtalk.go#L1657-L1693","documentation":"The DingTalk platform adapter refuses to send a proactive (unsolicited) message when the request lacks a target. A proactive send to a group needs the group's conversationId (openConversationId), while a direct send to a user needs the senderStaffId used to batch-send via userIds. The adapter builds the request payload only when one of these identifiers is present; otherwise it fails fast instead of calling the API with an empty target.","triggerScenarios":"Calling the platform's proactive send path (e.g. Send outside of an inbound-message reply context) where neither rc.conversationId (group) nor rc.senderStaffId (direct) was populated — for example a cron/timer notification with no chat context attached.","commonSituations":"Scheduled/timed messages built without a stored conversationId; forwarding an agent response through a session whose originating message metadata was dropped; config missing the chat binding for proactive notifications.","solutions":["Set the target identifier: pass the group conversationId (openConversationId) for group sends, or the user's senderStaffId/staffId for direct sends, in the message context or options used for the proactive send.","For scheduled sends, persist the conversationId (or staffId) from the first inbound interaction and reuse it when triggering the proactive message.","If the message is a reply to an inbound message, use the normal reply path (Reply) instead of the proactive send path so the target is inherited automatically."],"exampleFix":"// before\nrc := core.RequestContext{} // no conversationId, no senderStaffId\np.Send(ctx, rc, \"daily report\")\n// after\nrc := core.RequestContext{ConversationID: \"cidXXXXXXXX\"} // from stored chat binding\np.Send(ctx, rc, \"daily report\")","handlingStrategy":"validation","validationCode":"func canProactiveSend(rc RequestContext) error {\n    if rc.ConversationID == \"\" && rc.SenderStaffID == \"\" {\n        return errors.New(\"dingtalk: need conversationId or senderStaffId for proactive send\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Store the conversationId/staffId captured from the first inbound message for later proactive sends","Prefer the Reply path whenever responding to an inbound message","Add a startup self-check that validates configured chat bindings for scheduled messages"],"tags":["dingtalk","proactive-send","missing-target","configuration"],"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"}