From ec5d5c33bd574b6408da92ae63a77a1aed6cb67a Mon Sep 17 00:00:00 2001 From: "Molecule AI Dev Engineer A (Kimi)" Date: Tue, 2 Jun 2026 04:26:58 +0000 Subject: [PATCH] fix(bundle,channels,handlers): log unchecked RecordAndBroadcast, bot.Send, and DB errors Adds error logging for silently-discarded returns in: - bundle/importer.go: broadcast on provision-failed - channels/manager.go: broadcast on inbound/outbound messages - channels/telegram.go: bot.Send callback ack and edit message - handlers/approvals.go: broadcast on approval create/escalate/decide Does not change control flow; purely observability. Co-Authored-By: Claude Opus 4.7 --- workspace-server/internal/bundle/importer.go | 6 +++-- workspace-server/internal/channels/manager.go | 12 ++++++---- .../internal/channels/telegram.go | 8 +++++-- .../internal/handlers/approvals.go | 22 +++++++++++++------ 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/workspace-server/internal/bundle/importer.go b/workspace-server/internal/bundle/importer.go index d8ce175be..99701903a 100644 --- a/workspace-server/internal/bundle/importer.go +++ b/workspace-server/internal/bundle/importer.go @@ -149,9 +149,11 @@ func markFailed(ctx context.Context, wsID string, broadcaster *events.Broadcaste models.StatusFailed, msg, wsID); dbErr != nil { log.Printf("bundle import: failed to mark workspace %s as failed: %v", wsID, dbErr) } - broadcaster.RecordAndBroadcast(ctx, string(events.EventWorkspaceProvisionFailed), wsID, map[string]interface{}{ + if bcErr := broadcaster.RecordAndBroadcast(ctx, string(events.EventWorkspaceProvisionFailed), wsID, map[string]interface{}{ "error": msg, - }) + }); bcErr != nil { + log.Printf("bundle import: failed to broadcast provision failed for %s: %v", wsID, bcErr) + } } func nilIfEmpty(s string) interface{} { diff --git a/workspace-server/internal/channels/manager.go b/workspace-server/internal/channels/manager.go index b4c90c0fd..1721593ec 100644 --- a/workspace-server/internal/channels/manager.go +++ b/workspace-server/internal/channels/manager.go @@ -407,12 +407,14 @@ func (m *Manager) HandleInbound(ctx context.Context, ch ChannelRow, msg *Inbound // Broadcast event if m.broadcaster != nil { - m.broadcaster.RecordAndBroadcast(ctx, string(events.EventChannelMessage), ch.WorkspaceID, map[string]interface{}{ + if err := m.broadcaster.RecordAndBroadcast(ctx, string(events.EventChannelMessage), ch.WorkspaceID, map[string]interface{}{ "channel_id": ch.ID, "channel_type": ch.ChannelType, "username": msg.Username, "direction": "inbound", - }) + }); err != nil { + log.Printf("Channels: failed to broadcast inbound event: %v", err) + } } return nil @@ -453,11 +455,13 @@ func (m *Manager) SendOutbound(ctx context.Context, channelID string, text strin } if m.broadcaster != nil { - m.broadcaster.RecordAndBroadcast(ctx, string(events.EventChannelMessage), ch.WorkspaceID, map[string]interface{}{ + if err := m.broadcaster.RecordAndBroadcast(ctx, string(events.EventChannelMessage), ch.WorkspaceID, map[string]interface{}{ "channel_id": ch.ID, "channel_type": ch.ChannelType, "direction": "outbound", - }) + }); err != nil { + log.Printf("Channels: failed to broadcast outbound event: %v", err) + } } return nil diff --git a/workspace-server/internal/channels/telegram.go b/workspace-server/internal/channels/telegram.go index 3d323057c..11e59ee57 100644 --- a/workspace-server/internal/channels/telegram.go +++ b/workspace-server/internal/channels/telegram.go @@ -517,7 +517,9 @@ func (t *TelegramAdapter) StartPolling(ctx context.Context, config map[string]in // Acknowledge the button press (removes loading spinner) ackCfg := tgbotapi.NewCallback(cb.ID, "Received") - bot.Send(ackCfg) + if _, err := bot.Send(ackCfg); err != nil { + log.Printf("telegram: failed to send callback ack: %v", err) + } // Update the message to show what was clicked decision := "approved" @@ -529,7 +531,9 @@ func (t *TelegramAdapter) StartPolling(ctx context.Context, config map[string]in cb.Message.MessageID, cb.Message.Text+"\n\n✅ CEO "+decision, ) - bot.Send(editMsg) + if _, err := bot.Send(editMsg); err != nil { + log.Printf("telegram: failed to send edit message: %v", err) + } // Route the decision as an inbound message to the agent inbound := &InboundMessage{ diff --git a/workspace-server/internal/handlers/approvals.go b/workspace-server/internal/handlers/approvals.go index 985010cf8..40393c9f6 100644 --- a/workspace-server/internal/handlers/approvals.go +++ b/workspace-server/internal/handlers/approvals.go @@ -54,23 +54,29 @@ func (h *ApprovalsHandler) Create(c *gin.Context) { return } - h.broadcaster.RecordAndBroadcast(ctx, string(events.EventApprovalRequested), workspaceID, map[string]interface{}{ + if err := h.broadcaster.RecordAndBroadcast(ctx, string(events.EventApprovalRequested), workspaceID, map[string]interface{}{ "approval_id": approvalID, "action": body.Action, "reason": body.Reason, "task_id": body.TaskID, - }) + }); err != nil { + log.Printf("approvals: failed to broadcast approval requested: %v", err) + } // Auto-escalate to parent var parentID *string - db.DB.QueryRowContext(ctx, `SELECT parent_id FROM workspaces WHERE id = $1`, workspaceID).Scan(&parentID) + if err := db.DB.QueryRowContext(ctx, `SELECT parent_id FROM workspaces WHERE id = $1`, workspaceID).Scan(&parentID); err != nil { + log.Printf("approvals: failed to lookup parent for escalation: %v", err) + } if parentID != nil { - h.broadcaster.RecordAndBroadcast(ctx, string(events.EventApprovalEscalated), *parentID, map[string]interface{}{ + if err := h.broadcaster.RecordAndBroadcast(ctx, string(events.EventApprovalEscalated), *parentID, map[string]interface{}{ "approval_id": approvalID, "from_workspace_id": workspaceID, "action": body.Action, "reason": body.Reason, - }) + }); err != nil { + log.Printf("approvals: failed to broadcast approval escalated: %v", err) + } } c.JSON(http.StatusCreated, gin.H{"approval_id": approvalID, "status": "pending"}) @@ -221,11 +227,13 @@ func (h *ApprovalsHandler) Decide(c *gin.Context) { eventType = "APPROVAL_DENIED" } - h.broadcaster.RecordAndBroadcast(ctx, eventType, workspaceID, map[string]interface{}{ + if err := h.broadcaster.RecordAndBroadcast(ctx, eventType, workspaceID, map[string]interface{}{ "approval_id": approvalID, "decision": body.Decision, "decided_by": decidedBy, - }) + }); err != nil { + log.Printf("approvals: failed to broadcast approval decision: %v", err) + } c.JSON(http.StatusOK, gin.H{"status": body.Decision, "approval_id": approvalID}) } -- 2.52.0