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

  1. Ensure only onChatBotMessageReceived writes to sessionWebhooks, and only with the string data.SessionWebhook
  2. 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

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


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/eee50714e4219de0. Report an issue: GitHub.