From dcf9ec4d496c60f1b392f79ac1c29c584b69e1d6 Mon Sep 17 00:00:00 2001 From: viettranx Date: Thu, 12 Mar 2026 09:36:11 +0700 Subject: [PATCH] fix: handle DB errors in credential merge to prevent silent data loss loadExistingCreds now distinguishes sql.ErrNoRows (safe empty map) from real DB errors (propagated to caller). Prevents transient DB failures from silently wiping existing credentials during partial updates. --- internal/store/pg/channel_instances.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/internal/store/pg/channel_instances.go b/internal/store/pg/channel_instances.go index 0c4c829b..66e9f00a 100644 --- a/internal/store/pg/channel_instances.go +++ b/internal/store/pg/channel_instances.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "encoding/json" + "errors" "fmt" "log/slog" "strings" @@ -172,7 +173,10 @@ func (s *PGChannelInstanceStore) Update(ctx context.Context, id uuid.UUID, updat // Merge with existing credentials so partial updates don't wipe other fields if len(newCreds) > 0 { - existing, _ := s.loadExistingCreds(ctx, id) + existing, err := s.loadExistingCreds(ctx, id) + if err != nil { + return fmt.Errorf("load existing credentials for merge: %w", err) + } for k, v := range newCreds { existing[k] = v } @@ -200,8 +204,11 @@ func (s *PGChannelInstanceStore) Update(ctx context.Context, id uuid.UUID, updat func (s *PGChannelInstanceStore) loadExistingCreds(ctx context.Context, id uuid.UUID) (map[string]any, error) { var raw []byte err := s.db.QueryRowContext(ctx, "SELECT credentials FROM channel_instances WHERE id = $1", id).Scan(&raw) - if err != nil || len(raw) == 0 { - return make(map[string]any), err + if errors.Is(err, sql.ErrNoRows) || len(raw) == 0 { + return make(map[string]any), nil + } + if err != nil { + return nil, err } if s.encKey != "" { if dec, err := crypto.Decrypt(string(raw), s.encKey); err == nil {