From 355b3bc7dae1f1d608d5caa33120fffdbb7887da Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Wed, 11 Feb 2026 15:35:18 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20fix:=20close=20organization=20ro?= =?UTF-8?q?ws=20before=20operator=20token=20upserts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Collect and close organization rows before executing membership upserts in the same transaction. This avoids lib/pq protocol errors during operator-access bootstrap and allows the operator token secret to be created. Signed-off-by: Thomas Kosiewski --- _Generated with [`mux`](https://github.com/coder/mux) • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `bash.00`_ Change-Id: I47d6a3c644be463a0b81c0720b05b61897200154 --- .../operator_access_postgres.go | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/internal/coderbootstrap/operator_access_postgres.go b/internal/coderbootstrap/operator_access_postgres.go index d97a22dc..acdac1d3 100644 --- a/internal/coderbootstrap/operator_access_postgres.go +++ b/internal/coderbootstrap/operator_access_postgres.go @@ -367,9 +367,32 @@ WHERE deleted = false if err != nil { return fmt.Errorf("list organizations: %w", err) } - defer func() { + + organizationIDs := make([]uuid.UUID, 0) + for rows.Next() { + var organizationID uuid.UUID + if err := rows.Scan(&organizationID); err != nil { + _ = rows.Close() + return fmt.Errorf("scan organization ID: %w", err) + } + if organizationID == uuid.Nil { + _ = rows.Close() + return fmt.Errorf("assertion failed: organization ID must not be nil") + } + + organizationIDs = append(organizationIDs, organizationID) + } + if err := rows.Err(); err != nil { _ = rows.Close() - }() + return fmt.Errorf("iterate organizations: %w", err) + } + if err := rows.Close(); err != nil { + return fmt.Errorf("close organizations rows: %w", err) + } + + if len(organizationIDs) == 0 { + return fmt.Errorf("assertion failed: expected at least one organization") + } const upsertOrganizationMemberQuery = ` INSERT INTO organization_members ( @@ -392,28 +415,11 @@ SET roles = ARRAY['organization-admin'] ` - organizationCount := 0 - for rows.Next() { - organizationCount++ - - var organizationID uuid.UUID - if err := rows.Scan(&organizationID); err != nil { - return fmt.Errorf("scan organization ID: %w", err) - } - if organizationID == uuid.Nil { - return fmt.Errorf("assertion failed: organization ID must not be nil") - } - + for _, organizationID := range organizationIDs { if _, execErr := tx.ExecContext(ctx, upsertOrganizationMemberQuery, organizationID, userID, now); execErr != nil { return fmt.Errorf("upsert operator organization membership for org %q: %w", organizationID.String(), execErr) } } - if err := rows.Err(); err != nil { - return fmt.Errorf("iterate organizations: %w", err) - } - if organizationCount == 0 { - return fmt.Errorf("assertion failed: expected at least one organization") - } return nil }