From 66b80fb81bb8d0936c127a924d70f62e73945639 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Wed, 27 May 2026 21:01:40 +0200 Subject: [PATCH] fix(clients): tolerate orphan client_inbounds rows in Delete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeleteByEmail's previous fix only covered the case where GetRecordByEmail returned ErrRecordNotFound. When the ClientRecord exists but a client_inbounds row points to an inbound that has been removed out-of-band (failed mid-delete, manual SQL, pre-SyncInbound migration), Delete bubbled the raw gorm "record not found" from inboundSvc.GetInbound and aborted before any cleanup ran — leaving the client un-deletable through the UI/API. Match the tolerance bulkDelInboundClients already has: when GetInbound returns gorm.ErrRecordNotFound for a join row, log a warning and continue. The unconditional Delete(&model.ClientInbound{}) later in the function then removes the stale row, and the ClientRecord delete succeeds. --- web/service/client.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/service/client.go b/web/service/client.go index 964bf0d5..9d0dba85 100644 --- a/web/service/client.go +++ b/web/service/client.go @@ -678,6 +678,10 @@ func (s *ClientService) Delete(inboundSvc *InboundService, id int, keepTraffic b for _, ibId := range inboundIds { inbound, getErr := inboundSvc.GetInbound(ibId) if getErr != nil { + if errors.Is(getErr, gorm.ErrRecordNotFound) { + logger.Warningf("Delete: skipping orphan client_inbounds row for client_id=%d, inbound_id=%d (inbound missing)", id, ibId) + continue + } return needRestart, getErr } key := clientKeyForProtocol(inbound.Protocol, existing)