sipeed/picoclaw · error
invalid session_webhook type for chat %s
Error message
invalid session_webhook type for chat %s
What it means
The value stored in sessionWebhooks for this ChatID failed the string type assertion. The only writer in the shipped code stores data.SessionWebhook (a string), so hitting this error means some other code path stored a non-string into the map — it is an internal invariant guard, not an expected runtime failure.
Source
Thrown at pkg/channels/dingtalk/dingtalk.go:124
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 arrives
// IChatBotMessageHandler is: func(c context.Context, data *chatbot.BotCallbackDataModel) ([]byte, error)
func (c *DingTalkChannel) onChatBotMessageReceived(
ctx context.Context,
data *chatbot.BotCallbackDataModel,
) ([]byte, error) {View on GitHub (pinned to 49183d7e8d)
Solutions
- Ensure only onChatBotMessageReceived writes to sessionWebhooks, and only with the string data.SessionWebhook
- Replace the sync.Map with a typed map[string]string guarded by a mutex so non-strings cannot be stored
Example fix
// before
var sessionWebhooks sync.Map // chatID -> anything
// after
type webhookTable struct {
mu sync.RWMutex
m map[string]string // chatID -> sessionWebhook
} Defensive patterns
Strategy: type-guard
Type guard
func sessionWebhookFor(m *sync.Map, chatID string) (string, bool) {
v, ok := m.Load(chatID)
if !ok {
return "", false
}
s, ok := v.(string)
return s, ok
} Try / catch
if webhook, ok := sessionWebhookFor(&ch.sessionWebhooks, msg.ChatID); !ok {
return fmt.Errorf("cannot reply to chat %s", msg.ChatID)
} else {
return ch.SendDirectReply(ctx, webhook, msg.Content)
} Prevention
- Restrict writes to sessionWebhooks to the inbound callback handler only
- Prefer a typed map[string]string with a mutex over sync.Map for string-only data
- Never reuse the webhook map for unrelated per-chat state
When it happens
Trigger: A fork or extension stores a non-string (struct, []byte, url.URL) into sessionWebhooks under a chatID key; only the type assertion in Send triggers it.
Common situations: Custom modifications reusing sessionWebhooks for extra per-chat data; practically unreachable in the stock code.
Related errors
- conversation not found for %s after ingest
- failed to decode serialized config: %w
- channel %s does not support reactions
- dingtalk client_id and client_secret are required
- failed to start stream client: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/eee50714e4219de0.
Report an issue: GitHub.