From 73eb3c7a85b918325f40011067157a955ee128a1 Mon Sep 17 00:00:00 2001 From: Molecule AI Fullstack Engineer Date: Wed, 13 May 2026 15:49:57 +0000 Subject: [PATCH] fix(handlers): add rows.Err() checks after all scan loops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Go's database/sql contract requires callers to check rows.Err() after a for rows.Next() loop — a mid-stream error (e.g. dropped connection mid-result-set) is not surfaced by rows.Next() returning false. Covered handlers: - delegation.go: ListDelegations - approvals.go: ListPendingApprovals, List - instructions.go: List handler, scanInstructions helper (interface extended) - secrets.go: ListSecrets, ListGlobalSecrets, notifyGlobalSecretChange - events.go: List, ListByWorkspace - discovery.go: queryPeerMaps All checks log the error (non-fatal) so callers continue to receive the partial result set rather than silently truncating. Refs #862 (extending scope beyond delegation.go) --- workspace-server/internal/handlers/approvals.go | 6 ++++++ workspace-server/internal/handlers/delegation.go | 3 +++ workspace-server/internal/handlers/discovery.go | 3 +++ workspace-server/internal/handlers/events.go | 6 ++++++ workspace-server/internal/handlers/instructions.go | 7 +++++++ workspace-server/internal/handlers/secrets.go | 9 +++++++++ 6 files changed, 34 insertions(+) diff --git a/workspace-server/internal/handlers/approvals.go b/workspace-server/internal/handlers/approvals.go index 1f091afa..b5b2f7c4 100644 --- a/workspace-server/internal/handlers/approvals.go +++ b/workspace-server/internal/handlers/approvals.go @@ -116,6 +116,9 @@ func (h *ApprovalsHandler) ListAll(c *gin.Context) { "created_at": createdAt, }) } + if err := rows.Err(); err != nil { + log.Printf("ListPendingApprovals scan error: %v", err) + } c.JSON(http.StatusOK, approvals) } @@ -155,6 +158,9 @@ func (h *ApprovalsHandler) List(c *gin.Context) { "created_at": createdAt, }) } + if err := rows.Err(); err != nil { + log.Printf("ListApprovals scan error: %v", err) + } c.JSON(http.StatusOK, approvals) } diff --git a/workspace-server/internal/handlers/delegation.go b/workspace-server/internal/handlers/delegation.go index 6761ec7e..0449dddd 100644 --- a/workspace-server/internal/handlers/delegation.go +++ b/workspace-server/internal/handlers/delegation.go @@ -645,6 +645,9 @@ func (h *DelegationHandler) ListDelegations(c *gin.Context) { } delegations = append(delegations, entry) } + if err := rows.Err(); err != nil { + log.Printf("ListDelegations scan error: %v", err) + } if delegations == nil { delegations = []map[string]interface{}{} diff --git a/workspace-server/internal/handlers/discovery.go b/workspace-server/internal/handlers/discovery.go index 48a3131a..6e0e79ac 100644 --- a/workspace-server/internal/handlers/discovery.go +++ b/workspace-server/internal/handlers/discovery.go @@ -352,6 +352,9 @@ func queryPeerMaps(query string, args ...interface{}) ([]map[string]interface{}, result = append(result, peer) } + if err := rows.Err(); err != nil { + log.Printf("queryPeerMaps scan error: %v", err) + } return result, nil } diff --git a/workspace-server/internal/handlers/events.go b/workspace-server/internal/handlers/events.go index d297026b..5667a20f 100644 --- a/workspace-server/internal/handlers/events.go +++ b/workspace-server/internal/handlers/events.go @@ -49,6 +49,9 @@ func (h *EventsHandler) List(c *gin.Context) { "created_at": createdAt, }) } + if err := rows.Err(); err != nil { + log.Printf("ListEvents scan error: %v", err) + } c.JSON(http.StatusOK, events) } @@ -87,5 +90,8 @@ func (h *EventsHandler) ListByWorkspace(c *gin.Context) { "created_at": createdAt, }) } + if err := rows.Err(); err != nil { + log.Printf("ListEventsByWorkspace scan error: %v", err) + } c.JSON(http.StatusOK, events) } diff --git a/workspace-server/internal/handlers/instructions.go b/workspace-server/internal/handlers/instructions.go index 2e8e89ac..eb7ad4c3 100644 --- a/workspace-server/internal/handlers/instructions.go +++ b/workspace-server/internal/handlers/instructions.go @@ -248,6 +248,9 @@ func (h *InstructionsHandler) Resolve(c *gin.Context) { b.WriteString(content) b.WriteString("\n\n") } + if err := rows.Err(); err != nil { + log.Printf("ListInstructions scan error: %v", err) + } c.JSON(http.StatusOK, gin.H{ "workspace_id": workspaceID, @@ -258,6 +261,7 @@ func (h *InstructionsHandler) Resolve(c *gin.Context) { func scanInstructions(rows interface { Next() bool Scan(dest ...interface{}) error + Err() error }) []Instruction { var instructions []Instruction for rows.Next() { @@ -269,6 +273,9 @@ func scanInstructions(rows interface { } instructions = append(instructions, inst) } + if err := rows.Err(); err != nil { + log.Printf("Instructions scan loop error: %v", err) + } if instructions == nil { instructions = []Instruction{} } diff --git a/workspace-server/internal/handlers/secrets.go b/workspace-server/internal/handlers/secrets.go index 43a8a0d7..8b6754c5 100644 --- a/workspace-server/internal/handlers/secrets.go +++ b/workspace-server/internal/handlers/secrets.go @@ -63,6 +63,9 @@ func (h *SecretsHandler) List(c *gin.Context) { "updated_at": updatedAt, }) } + if err := rows.Err(); err != nil { + log.Printf("ListSecrets scan error: %v", err) + } // 2. Global secrets not overridden at workspace level globalRows, err := db.DB.QueryContext(ctx, @@ -324,6 +327,9 @@ func (h *SecretsHandler) ListGlobal(c *gin.Context) { "scope": "global", }) } + if err := rows.Err(); err != nil { + log.Printf("ListGlobalSecrets scan error: %v", err) + } c.JSON(http.StatusOK, secrets) } @@ -400,6 +406,9 @@ func (h *SecretsHandler) restartAllAffectedByGlobalKey(key string) { ids = append(ids, id) } } + if err := rows.Err(); err != nil { + log.Printf("notifyGlobalSecretChange scan error: %v", err) + } if len(ids) == 0 { return }