diff --git a/frontend/src/components/account/EditAccountModal.vue b/frontend/src/components/account/EditAccountModal.vue index 9bc4f9167a..04b1c9f68f 100644 --- a/frontend/src/components/account/EditAccountModal.vue +++ b/frontend/src/components/account/EditAccountModal.vue @@ -4771,7 +4771,7 @@ const syncAntigravityUpstreamModels = async () => { appStore.showWarning(t('admin.accounts.syncUpstreamModelsMetadataPartial')) } } catch (error) { - const message = error instanceof Error ? error.message : t('admin.accounts.syncUpstreamModelsFailed') + const message = extractApiErrorMessage(error, t('admin.accounts.syncUpstreamModelsFailed')) appStore.showError(t('admin.accounts.syncUpstreamModelsError', { message })) } finally { isSyncingAntigravityUpstream.value = false diff --git a/frontend/src/components/account/ModelWhitelistSelector.vue b/frontend/src/components/account/ModelWhitelistSelector.vue index d953c21ce9..aa09de7bbb 100644 --- a/frontend/src/components/account/ModelWhitelistSelector.vue +++ b/frontend/src/components/account/ModelWhitelistSelector.vue @@ -155,6 +155,7 @@ import ModelIcon from '@/components/common/ModelIcon.vue' import Icon from '@/components/icons/Icon.vue' import { allModels, getModelsByPlatform } from '@/composables/useModelWhitelist' import { supportsUpstreamModelSync } from '@/constants/platformCatalog' +import { extractApiErrorMessage } from '@/utils/apiError' const { t } = useI18n() @@ -343,7 +344,7 @@ const syncUpstreamModels = async () => { appStore.showWarning(t('admin.accounts.syncUpstreamModelsMetadataPartial')) } } catch (error) { - const message = error instanceof Error ? error.message : t('admin.accounts.syncUpstreamModelsFailed') + const message = extractApiErrorMessage(error, t('admin.accounts.syncUpstreamModelsFailed')) appStore.showError(t('admin.accounts.syncUpstreamModelsError', { message })) } finally { isSyncingUpstream.value = false diff --git a/frontend/src/components/account/__tests__/ModelWhitelistSelector.spec.ts b/frontend/src/components/account/__tests__/ModelWhitelistSelector.spec.ts index 9e27d29db9..0b5f10a015 100644 --- a/frontend/src/components/account/__tests__/ModelWhitelistSelector.spec.ts +++ b/frontend/src/components/account/__tests__/ModelWhitelistSelector.spec.ts @@ -29,7 +29,7 @@ vi.mock('vue-i18n', async () => { return { ...actual, useI18n: () => ({ - t: (key: string, params?: Record) => key === 'common.copy' ? '复制' : key === 'admin.accounts.modelMappingConflict' ? `Model mapping conflict: ${params?.from} → ${params?.to}` : key + t: (key: string, params?: Record) => key === 'common.copy' ? '复制' : key === 'admin.accounts.modelMappingConflict' ? `Model mapping conflict: ${params?.from} → ${params?.to}` : key === 'admin.accounts.syncUpstreamModelsError' ? `${key}: ${params?.message}` : key }) } }) @@ -244,6 +244,26 @@ describe('ModelWhitelistSelector', () => { expect(showSuccess).not.toHaveBeenCalled() }) + it('shows the backend message when upstream sync is rejected', async () => { + syncUpstreamModels.mockRejectedValue({ + status: 400, + code: 'BAD_REQUEST', + message: 'Vertex API key accounts do not support upstream model listing' + }) + const wrapper = mountSelector({ platform: 'gemini', accountId: 174 }) + + const syncButton = wrapper + .findAll('button') + .find(button => button.text() === 'admin.accounts.syncUpstreamModels') + expect(syncButton).toBeDefined() + await syncButton!.trigger('click') + await flushPromises() + + expect(showError).toHaveBeenCalledWith( + 'admin.accounts.syncUpstreamModelsError: Vertex API key accounts do not support upstream model listing' + ) + }) + it('shows success and a partial warning when some capabilities were saved', async () => { syncUpstreamModels.mockResolvedValue({ models: ['gpt-6-astra', 'gpt-image-2'],