bytebase/bytebase · error
%s
Error message
%s
What it means
Lark's webhook endpoint returned 200 with parseable JSON, but the response Code field is non-zero, indicating Lark rejected the message. The error surfaces Lark's own Message field (e.g. 'key words not found' or rate-limit text). This is a Lark-side business error, not a transport failure.
Source
Thrown at backend/plugin/webhook/lark/lark.go:219
}
b, err := io.ReadAll(resp.Body)
if err != nil {
return errors.Wrapf(err, "failed to read POST webhook response from %s", context.URL)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return errors.Errorf("failed to POST webhook %s, status code: %d, response body: %s", context.URL, resp.StatusCode, b)
}
webhookResponse := &WebhookResponse{}
if err := json.Unmarshal(b, webhookResponse); err != nil {
return errors.Wrapf(err, "malformed webhook response from %s", context.URL)
}
if webhookResponse.Code != 0 {
return errors.Errorf("%s", webhookResponse.Message)
}
return nil
}
func getMessageCard(context webhook.Context) *WebhookCard {
var markdownBuf strings.Builder
if context.Description != "" {
_, _ = fmt.Fprintf(&markdownBuf, "%s\n", context.Description)
}
for _, meta := range context.GetMetaList() {
_, _ = fmt.Fprintf(&markdownBuf, "**%s**: %s\n", meta.Name, meta.Value)
}
if context.ActorName != "" {
_, _ = fmt.Fprintf(&markdownBuf, "**Actor**: %s (%s)\n", context.ActorName, context.ActorEmail)View on GitHub (pinned to 1870550677)
Solutions
- Read the surfaced Message text — it names the exact Lark-side cause.
- If the message is 'key words not found', either add the configured keyword to Bytebase's message or remove the keyword requirement in the Lark bot settings.
- If the message indicates an invalid sign, re-check the webhook secret/token and ensure server clock sync (NTP).
- If rate-limited, add throttling/queueing for webhook deliveries and retry with backoff.
- Verify the bot still exists and is enabled in the Lark group; recreate the webhook if revoked.
Example fix
// before: message may not contain the Lark bot keyword "title": "Pipeline succeeded" // after: include the configured keyword in the card text "title": "[Bytebase] Pipeline succeeded"
Defensive patterns
Strategy: validation
Validate before calling
// ensure the configured keyword appears in the outgoing message text
if larkKeyword != "" && !strings.Contains(cardText, larkKeyword) {
return fmt.Errorf("message must contain Lark bot keyword %q", larkKeyword)
} Type guard
null
Try / catch
if err := postMessage(...); err != nil {
msg := strings.TrimPrefix(err.Error(), "")
switch {
case strings.Contains(msg, "key words not found"):
// fix keyword configuration
case strings.Contains(msg, "sign"):
// fix secret / clock sync
default:
// rate limit or other: retry with backoff
}
} Prevention
- Configure the Lark bot keyword to match a fixed prefix Bytebase includes in every card.
- If using signed webhooks, keep the secret in sync and run NTP on the server.
- Throttle sends to stay under Lark's per-bot rate limit (~5/sec).
- Keep the webhook active — recreate and update config if the bot is removed from the group.
When it happens
Trigger: postMessage gets Code != 0 in the WebhookResponse — most commonly Lark's 'key words not found' security policy (message must contain a configured keyword), invalid sign (signature mismatch for signature-enabled bots), or exceeding frequency limits.
Common situations: Lark bot configured with a custom keyword but the Bytebase message doesn't contain it; signature verification enabled in Lark but Bytebase signs with a wrong secret or clock drift; sending too many notifications per second (Lark limits ~5/s per bot).
Related errors
- failed to POST webhook %s, status code: %d, response body: %
- failed to get user id by email
- failed to do send message request
- failed to construct %s %s
- %s %s
AI-assisted analysis of bytebase/bytebase@1870550677 (2026-09-06).
Data as JSON: /api/errors/104e99c9c4973e63.
Report an issue: GitHub.