@@ -176,15 +176,10 @@ public void updateApiKey(@NonNull UUID id, @NonNull ProviderApiKeyUpdate provide
176176
177177 var authConfigUpdate = resolveAuthConfigUpdate (providerApiKeyUpdate , providerApiKey );
178178
179- var update = providerApiKeyUpdate ;
180- if (authConfigUpdate .authConfig () != null && update .apiKey () == null ) {
181- update = update .toBuilder ().apiKey (EncryptionUtils .encrypt ("" )).build ();
182- }
183-
184179 repository .update (providerApiKey .id (),
185180 workspaceId ,
186181 userName ,
187- update ,
182+ authConfigUpdate . effectiveUpdate () ,
188183 authConfigUpdate .clear (),
189184 authConfigUpdate .authConfig ());
190185
@@ -228,7 +223,15 @@ private ProviderAuthConfig resolveAuthConfigForTest(ProviderAuthCheck request, S
228223 return mergeSecretSentinels (incoming , stored );
229224 }
230225
231- private record AuthConfigUpdate (boolean clear , ProviderAuthConfig authConfig ) {
226+ /**
227+ * The resolved auth_config decision plus the effective update to persist. The api_key /
228+ * auth_config mutual exclusion is enforced entirely inside
229+ * {@link #resolveAuthConfigUpdate}: it both validates the incoming pair and blanks the
230+ * stored key when the update switches to token auth, so callers persist
231+ * {@code effectiveUpdate} as-is and cannot hold the invariant wrong.
232+ */
233+ private record AuthConfigUpdate (ProviderApiKeyUpdate effectiveUpdate , boolean clear ,
234+ ProviderAuthConfig authConfig ) {
232235 }
233236
234237 /**
@@ -241,10 +244,10 @@ private AuthConfigUpdate resolveAuthConfigUpdate(ProviderApiKeyUpdate update, Pr
241244 if (incoming == null ) {
242245 validateNoStaticKeyConflict (
243246 update .apiKey () != null ? update .apiKey () : stored .apiKey (), stored .authConfig ());
244- return new AuthConfigUpdate (false , null );
247+ return new AuthConfigUpdate (update , false , null );
245248 }
246249 if (incoming .isEmpty ()) {
247- return new AuthConfigUpdate (true , null );
250+ return new AuthConfigUpdate (update , true , null );
248251 }
249252 if (!stored .provider ().supportsDynamicTokenAuth ()) {
250253 throw new BadRequestException (
@@ -254,9 +257,15 @@ private AuthConfigUpdate resolveAuthConfigUpdate(ProviderApiKeyUpdate update, Pr
254257 if (!errors .isEmpty ()) {
255258 throw new BadRequestException (String .join ("; " , errors ));
256259 }
260+ // only an api_key set in this same request conflicts: an update carrying none switches
261+ // the provider to token auth, which implicitly clears the stored static key below —
262+ // the dialog hides the key field in token mode, so the swap must be self-contained
257263 validateNoStaticKeyConflict (update .apiKey (), incoming );
258264 var merged = mergeSecretSentinels (incoming , stored .authConfig ());
259- return new AuthConfigUpdate (false , merged );
265+ var effectiveUpdate = update .apiKey () == null
266+ ? update .toBuilder ().apiKey (EncryptionUtils .encrypt ("" )).build ()
267+ : update ;
268+ return new AuthConfigUpdate (effectiveUpdate , false , merged );
260269 }
261270
262271 private void validateNoStaticKeyConflict (String encryptedApiKey , ProviderAuthConfig authConfig ) {
0 commit comments