From 5bbf60bc4b52ac03d2b55adc87b33530a0d4d0a4 Mon Sep 17 00:00:00 2001 From: "Molecule AI Dev Engineer A (Kimi)" Date: Sun, 31 May 2026 23:47:39 +0000 Subject: [PATCH 1/2] fix(registry,crud,restart): defer rows.Close() to prevent cursor leaks - workspace_crud.go: defer descRows.Close() in cascade-delete descendant query - orphan_sweeper.go: defer knownRows.Close() in wiped-DB reverse-lookup - restart_context.go: defer rows.Close() in global + workspace secret key scans All sites previously used explicit Close() which leaks on panic or early-return. Defers close at block exit regardless of control flow. Co-Authored-By: Claude Opus 4.7 --- workspace-server/internal/handlers/restart_context.go | 10 ++++++++-- workspace-server/internal/handlers/workspace_crud.go | 2 +- workspace-server/internal/registry/orphan_sweeper.go | 4 +--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/workspace-server/internal/handlers/restart_context.go b/workspace-server/internal/handlers/restart_context.go index 3ae2f41d2..0df13d8fd 100644 --- a/workspace-server/internal/handlers/restart_context.go +++ b/workspace-server/internal/handlers/restart_context.go @@ -133,24 +133,30 @@ func loadRestartContextData(ctx context.Context, workspaceID string) restartCont // message bus. keySet := map[string]struct{}{} if rows, err := db.DB.QueryContext(ctx, `SELECT key FROM global_secrets`); err == nil { + defer rows.Close() for rows.Next() { var k string if rows.Scan(&k) == nil { keySet[k] = struct{}{} } } - rows.Close() + if err := rows.Err(); err != nil { + log.Printf("restart-context: global_secrets rows error: %v", err) + } } if rows, err := db.DB.QueryContext(ctx, `SELECT key FROM workspace_secrets WHERE workspace_id = $1`, workspaceID, ); err == nil { + defer rows.Close() for rows.Next() { var k string if rows.Scan(&k) == nil { keySet[k] = struct{}{} } } - rows.Close() + if err := rows.Err(); err != nil { + log.Printf("restart-context: workspace_secrets rows error: %v", err) + } } for k := range keySet { d.EnvKeys = append(d.EnvKeys, k) diff --git a/workspace-server/internal/handlers/workspace_crud.go b/workspace-server/internal/handlers/workspace_crud.go index e1a35793c..fbedce2ed 100644 --- a/workspace-server/internal/handlers/workspace_crud.go +++ b/workspace-server/internal/handlers/workspace_crud.go @@ -435,13 +435,13 @@ func (h *WorkspaceHandler) CascadeDelete(ctx context.Context, id string) ([]stri if err != nil { return nil, nil, fmt.Errorf("descendant query: %w", err) } + defer descRows.Close() for descRows.Next() { var descID string if descRows.Scan(&descID) == nil { descendantIDs = append(descendantIDs, descID) } } - descRows.Close() allIDs := append([]string{id}, descendantIDs...) diff --git a/workspace-server/internal/registry/orphan_sweeper.go b/workspace-server/internal/registry/orphan_sweeper.go index 6e4110cbc..b5ca08e81 100644 --- a/workspace-server/internal/registry/orphan_sweeper.go +++ b/workspace-server/internal/registry/orphan_sweeper.go @@ -258,6 +258,7 @@ func sweepLabeledOrphansWithoutRows(ctx context.Context, reaper OrphanReaper) { log.Printf("Orphan sweeper: wiped-DB reverse-lookup failed: %v — skipping wiped-DB pass", err) return } + defer knownRows.Close() known := make(map[string]struct{}, len(managedLikes)) for knownRows.Next() { var lk string @@ -267,9 +268,6 @@ func sweepLabeledOrphansWithoutRows(ctx context.Context, reaper OrphanReaper) { } known[lk] = struct{}{} } - if cerr := knownRows.Close(); cerr != nil { - log.Printf("Orphan sweeper: wiped-DB rows close failed: %v", cerr) - } if iterErr := knownRows.Err(); iterErr != nil { log.Printf("Orphan sweeper: wiped-DB rows iteration failed: %v", iterErr) return -- 2.52.0 From a7d81049797b3ac5478c6198d7ffdcd2994d4539 Mon Sep 17 00:00:00 2001 From: "Molecule AI Dev Engineer A (Kimi)" Date: Mon, 1 Jun 2026 12:36:44 +0000 Subject: [PATCH 2/2] ci: remove unused canvasUserMessage type to fix lint on staging internal/handlers/a2a_proxy_helpers.go:412 had an unused struct that causes golangci-lint `unused` failure on every PR targeting staging. Co-Authored-By: Claude Opus 4.7 --- workspace-server/internal/handlers/a2a_proxy_helpers.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/workspace-server/internal/handlers/a2a_proxy_helpers.go b/workspace-server/internal/handlers/a2a_proxy_helpers.go index 98c51bb7d..11916e6b1 100644 --- a/workspace-server/internal/handlers/a2a_proxy_helpers.go +++ b/workspace-server/internal/handlers/a2a_proxy_helpers.go @@ -407,15 +407,6 @@ func validateCallerToken(ctx context.Context, c *gin.Context, callerID string) e // matching (the wsauth errors are typed for the invalid case). var errInvalidCallerToken = errors.New("missing caller auth token") -// canvasUserMessage holds the extracted user message extracted from an -// A2A canvas request body for broadcasting to other sessions. -type canvasUserMessage struct { - Message string `json:"message,omitempty"` - Parts []map[string]interface{} `json:"parts,omitempty"` - MessageID string `json:"messageId,omitempty"` - Attachments []map[string]interface{} `json:"attachments,omitempty"` -} - // extractCanvasUserMessage parses an A2A JSON-RPC request body and extracts // the user-authored text and attachments from a canvas-initiated message/send. // Returns nil when the body is not a canvas user message (empty, malformed, -- 2.52.0