sipeed/picoclaw · error
no session_webhook found for chat %s, cannot send message
Error message
no session_webhook found for chat %s, cannot send message
What it means
DingTalk replies are delivered through a per-conversation sessionWebhook captured from each inbound bot callback (stored in the in-memory sync.Map in onChatBotMessageReceived). Send fails with this error when no webhook was ever stored for msg.ChatID, i.e. the bot has no way to address that chat because it never saw an inbound message for it in this process lifetime.
Source
Thrown at pkg/channels/dingtalk/dingtalk.go:119
if c.streamClient != nil {
c.streamClient.Close()
}
c.SetRunning(false)
logger.InfoC("dingtalk", "DingTalk channel stopped")
return nil
}
// Send sends a message to DingTalk via the chatbot reply API
func (c *DingTalkChannel) Send(ctx context.Context, msg bus.OutboundMessage) ([]string, error) {
if !c.IsRunning() {
return nil, channels.ErrNotRunning
}
// Get session webhook from storage
sessionWebhookRaw, ok := c.sessionWebhooks.Load(msg.ChatID)
if !ok {
return nil, fmt.Errorf("no session_webhook found for chat %s, cannot send message", msg.ChatID)
}
sessionWebhook, ok := sessionWebhookRaw.(string)
if !ok {
return nil, fmt.Errorf("invalid session_webhook type for chat %s", msg.ChatID)
}
logger.DebugCF("dingtalk", "Sending message", map[string]any{
"chat_id": msg.ChatID,
"preview": utils.Truncate(msg.Content, 100),
})
// Use the session webhook to send the reply
return nil, c.SendDirectReply(ctx, sessionWebhook, msg.Content)
}
// onChatBotMessageReceived implements the IChatBotMessageHandler function signature
// This is called by the Stream SDK when a new message arrivesView on GitHub (pinned to 49183d7e8d)
Solutions
- Have the user send a fresh inbound message first — each callback refreshes the webhook for that conversation
- Use the exact ChatID from the InboundContext (ConversationId, or the senderId fallback for direct chats with ConversationType == "1")
- For proactive/late replies, capture the webhook from the inbound message's ReplyHandles["session_webhook"] and persist it yourself
- Do not design DingTalk flows that require pushing to unseen chats; the stream-mode bot is reply-only
Example fix
// before
_ = ch.Send(ctx, bus.OutboundMessage{ChatID: guessedChatID, Content: "hi"}) // no webhook -> error
// after: reply using the handle captured from the inbound message
if hook, ok := inbound.ReplyHandles["session_webhook"]; ok {
_ = ch.SendDirectReply(ctx, hook, "hi")
} Defensive patterns
Strategy: validation
Validate before calling
// Verify a webhook exists for the chat before attempting Send
if _, ok := ch.HasSessionWebhook(chatID); !ok {
// ask the user to send a message first; proactive send is not possible
return fmt.Errorf("no webhook for %s; user must message the bot first", chatID)
} Try / catch
if _, err := ch.Send(ctx, msg); err != nil {
if strings.Contains(err.Error(), "no session_webhook found") {
// permanent for this chat until a new inbound message arrives: do not retry blindly
notifyUserToSendMessage(chatID)
return nil
}
return err
} Prevention
- Design DingTalk flows as reply-only: always answer from a fresh inbound message
- Capture ReplyHandles["session_webhook"] from the inbound context and persist it if you need later replies
- Expect webhooks to be lost on restart (in-memory sync.Map) and short-lived over time
- Use the exact InboundContext.ChatID when constructing outbound messages
When it happens
Trigger: Calling Send with a ChatID that never messaged the bot (proactive push), after a process restart (sync.Map wiped), with a mismatched ID (senderId vs conversationId), or after the stored webhook entry was never written because the inbound callback had an empty SessionWebhook.
Common situations: Trying to proactively broadcast to DingTalk chats (unsupported for stream-mode bots), replying after the bot process restarted, session webhooks being short-lived so stale entries do not help replies hours later, using the wrong ID kind as ChatID.
Related errors
- dingtalk send: %w
- weixin send: %w: missing context token for chat %s
- dingtalk client_id and client_secret are required
- failed to start stream client: %w
- invalid session_webhook type for chat %s
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/b8c9e0cc16a7e1fd.
Report an issue: GitHub.