From 804b2ce26522a471fda7e2db57070ed5ceaae4d6 Mon Sep 17 00:00:00 2001 From: "Molecule AI Dev Engineer A (Kimi)" Date: Tue, 26 May 2026 13:24:52 +0000 Subject: [PATCH 1/2] fix(main,channels,webhooks): handle RowsAffected errors in background paths Fixes ignored result.RowsAffected() errors in: - main.go activity log cleanup: log error instead of silent skip - channels/manager.go telegram disable: log error instead of silent skip - webhooks.go cron triggers (issues/opened, pull_request_review): log error instead of mis-reporting 0 schedules triggered Co-Authored-By: Claude Opus 4.7 --- workspace-server/cmd/server/main.go | 5 ++++- workspace-server/internal/channels/manager.go | 5 ++++- .../internal/handlers/webhooks.go | 20 +++++++++++++------ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/workspace-server/cmd/server/main.go b/workspace-server/cmd/server/main.go index e8918db2f..8075e3929 100644 --- a/workspace-server/cmd/server/main.go +++ b/workspace-server/cmd/server/main.go @@ -149,7 +149,10 @@ func main() { result, err := db.DB.ExecContext(ctx, `DELETE FROM activity_logs WHERE created_at < now() - ($1 || ' days')::interval`, retentionDays) if err != nil { log.Printf("Activity log cleanup error: %v", err) - } else if n, _ := result.RowsAffected(); n > 0 { + n, err := result.RowsAffected() + if err != nil { + log.Printf("Activity log cleanup RowsAffected error: %v", err) + } else if n > 0 { log.Printf("Activity log cleanup: purged %d old entries", n) } } diff --git a/workspace-server/internal/channels/manager.go b/workspace-server/internal/channels/manager.go index 5a30f09ce..b64336a5e 100644 --- a/workspace-server/internal/channels/manager.go +++ b/workspace-server/internal/channels/manager.go @@ -82,7 +82,10 @@ func NewManager(proxy A2AProxy, broadcaster Broadcaster) *Manager { log.Printf("Channels: failed to disable telegram chat_id=%s: %v", chatID, err) return } - if rows, _ := res.RowsAffected(); rows > 0 { + rows, err := res.RowsAffected() + if err != nil { + log.Printf("Channels: disable telegram RowsAffected error chat_id=%s: %v", chatID, err) + } else if rows > 0 { log.Printf("Channels: disabled %d telegram channel(s) for chat_id=%s (bot removed)", rows, chatID) // Reload so the in-memory poller map drops the now-disabled row. m.Reload(ctx) diff --git a/workspace-server/internal/handlers/webhooks.go b/workspace-server/internal/handlers/webhooks.go index 9747c83b4..8a7bf17a5 100644 --- a/workspace-server/internal/handlers/webhooks.go +++ b/workspace-server/internal/handlers/webhooks.go @@ -394,9 +394,13 @@ func (h *WebhookHandler) handleCronTriggerEvent(c *gin.Context, eventType string log.Printf("Webhook: cron trigger (issues/opened) DB error: %v", err) return true, fmt.Errorf("failed to trigger schedules: %w", err) } - affected, _ := result.RowsAffected() - log.Printf("Webhook: issues/opened in %s #%d by %s — triggered %d pick-up-work schedule(s)", - payload.Repository.FullName, payload.Issue.Number, payload.Sender.Login, affected) + affected, err := result.RowsAffected() + if err != nil { + log.Printf("Webhook: issues/opened RowsAffected error: %v", err) + } else { + log.Printf("Webhook: issues/opened in %s #%d by %s — triggered %d pick-up-work schedule(s)", + payload.Repository.FullName, payload.Issue.Number, payload.Sender.Login, affected) + } c.JSON(http.StatusOK, gin.H{ "status": "triggered", @@ -429,9 +433,13 @@ func (h *WebhookHandler) handleCronTriggerEvent(c *gin.Context, eventType string log.Printf("Webhook: cron trigger (pull_request_review/submitted) DB error: %v", err) return true, fmt.Errorf("failed to trigger schedules: %w", err) } - affected, _ := result.RowsAffected() - log.Printf("Webhook: pull_request_review/submitted in %s PR #%d by %s (state=%s) — triggered %d review schedule(s)", - payload.Repository.FullName, payload.PullRequest.Number, payload.Sender.Login, payload.Review.State, affected) + affected, err := result.RowsAffected() + if err != nil { + log.Printf("Webhook: pull_request_review/submitted RowsAffected error: %v", err) + } else { + log.Printf("Webhook: pull_request_review/submitted in %s PR #%d by %s (state=%s) — triggered %d review schedule(s)", + payload.Repository.FullName, payload.PullRequest.Number, payload.Sender.Login, payload.Review.State, affected) + } c.JSON(http.StatusOK, gin.H{ "status": "triggered", -- 2.52.0 From 735e34571cf97098c8285527d125e4520f1a0a1e Mon Sep 17 00:00:00 2001 From: "Molecule AI Dev Engineer A (Kimi)" Date: Tue, 26 May 2026 13:39:26 +0000 Subject: [PATCH 2/2] fix(main): correct control flow in activity log cleanup RowsAffected fix The previous commit accidentally dropped the `} else {` guard when refactoring the RowsAffected error handling. This caused result.RowsAffected() to be called unconditionally after a failed ExecContext, which would panic on a nil result in the background goroutine. Co-Authored-By: Claude Opus 4.7 --- workspace-server/cmd/server/main.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/workspace-server/cmd/server/main.go b/workspace-server/cmd/server/main.go index 8075e3929..04dff6d57 100644 --- a/workspace-server/cmd/server/main.go +++ b/workspace-server/cmd/server/main.go @@ -149,11 +149,13 @@ func main() { result, err := db.DB.ExecContext(ctx, `DELETE FROM activity_logs WHERE created_at < now() - ($1 || ' days')::interval`, retentionDays) if err != nil { log.Printf("Activity log cleanup error: %v", err) - n, err := result.RowsAffected() - if err != nil { - log.Printf("Activity log cleanup RowsAffected error: %v", err) - } else if n > 0 { - log.Printf("Activity log cleanup: purged %d old entries", n) + } else { + n, err := result.RowsAffected() + if err != nil { + log.Printf("Activity log cleanup RowsAffected error: %v", err) + } else if n > 0 { + log.Printf("Activity log cleanup: purged %d old entries", n) + } } } } -- 2.52.0