{"record":{"id":"a6f3f35cc66d94c1","repo":"zeroclaw-labs/zeroclaw","slug":"webhook-reply-failed-status-err","errorCode":null,"errorMessage":"webhook reply failed ({status}): {err}","messagePattern":"webhook reply failed \\((.+?)\\): (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/dingtalk.rs","lineNumber":194,"sourceCode":"        let body = serde_json::json!({\n            \"msgtype\": \"markdown\",\n            \"markdown\": {\n                \"title\": title,\n                \"text\": message.content,\n            }\n        });\n\n        let resp = self\n            .http_client()\n            .post(webhook_url)\n            .json(&body)\n            .send()\n            .await?;\n\n        if !resp.status().is_success() {\n            let status = resp.status();\n            let err = resp.text().await.unwrap_or_default();\n            anyhow::bail!(\"webhook reply failed ({status}): {err}\");\n        }\n\n        Ok(())\n    }\n\n    async fn listen(&self, tx: tokio::sync::mpsc::Sender<ChannelMessage>) -> anyhow::Result<()> {\n        ::zeroclaw_log::record!(\n            INFO,\n            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note),\n            \"registering gateway connection...\"\n        );\n\n        let gw = self.register_connection().await?;\n        let ws_url = format!(\"{}?ticket={}\", gw.endpoint, gw.ticket);\n\n        ::zeroclaw_log::record!(\n            INFO,\n            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note),","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/dingtalk.rs#L176-L212","documentation":"DingTalkChannel::send replies by POSTing a markdown message to the per-chat sessionWebhook URL captured from the last inbound message (session_webhooks is keyed by both chat id and sender id). A non-2xx from that webhook becomes this error. DingTalk issues a fresh, short-lived webhook per inbound message, so expired sessions are the dominant cause; a chat with no stored session at all produces a different error (\"No session webhook found ...\").","triggerScenarios":"Replying long after the last inbound message, when the sessionWebhook has expired; group chat where the recipient key resolves to a different chat's stored URL; markdown payload rejected (empty text/title under msgtype markdown); webhook already consumed or rate-limited by an earlier reply.","commonSituations":"Scheduled/cron pushes to a chat that has not messaged recently; bot restarted (session_webhooks is in-memory) so old reply targets are gone; agent turns so long the reply lands after webhook expiry.","solutions":["Have the user send a new inbound message — the listen loop stores a fresh sessionWebhook — then retry the reply","Reply promptly within the session lifecycle instead of hours later","Verify message.recipient equals the reply_target the channel produced (conversation id for groups, senderStaffId for private chats)","Inspect the returned DingTalk body for the exact errorCode before assuming expiry"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"match dingtalk.send(&msg).await {\n    Err(e) if e.to_string().contains(\"webhook reply failed\") => {\n        // session webhook stale — queue the reply until the next inbound message re-establishes it\n    }\n    r => r?,\n}","preventionTips":["Send replies within the same session lifecycle as the inbound message","Remember session_webhooks is in-memory: after a restart every chat needs a fresh inbound message","Never build cron-push flows on DingTalk session webhooks — they are per-conversation and short-lived"],"tags":["dingtalk","webhook","session","messaging"],"backgroundTag":"expired-session-webhook","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}