From 2dc177d50b308850aa204d0b93211041fb320b3b Mon Sep 17 00:00:00 2001 From: fei <204683769+feiiiiii5@users.noreply.github.com> Date: Sat, 22 Aug 2026 22:56:39 +0800 Subject: [PATCH 1/5] FIX: distinguish catalog-unavailable from unregistered in Initializers page When the registered-initializer catalog request fails transiently, the Initializers page preserved configured baseline settings but described valid entries as 'Initializer is no longer registered.' A temporary metadata availability failure was therefore presented as a definitive registration problem. Track catalog fetch success/failure and pass the availability flag through to BaselineInitializers / AdditionalInitializers so unresolved names render 'Catalog metadata temporarily unavailable.' when only the catalog request failed. Names genuinely absent from a successful catalog response still show 'no longer registered.' Fixes #2442 Signed-off-by: fei <204683769+feiiiiii5@users.noreply.github.com> --- .../Initializers/AdditionalInitializers.tsx | 8 +++++++- .../Initializers/BaselineInitializers.tsx | 8 +++++++- .../components/Initializers/Initializers.tsx | 5 +++++ .../Initializers/initializerLookup.test.ts | 18 ++++++++++++++++++ .../Initializers/initializerLookup.ts | 11 +++++++++++ 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Initializers/AdditionalInitializers.tsx b/frontend/src/components/Initializers/AdditionalInitializers.tsx index 992292ca3c..d3a278218b 100644 --- a/frontend/src/components/Initializers/AdditionalInitializers.tsx +++ b/frontend/src/components/Initializers/AdditionalInitializers.tsx @@ -25,6 +25,7 @@ import ConfirmDialog from '../ConfirmDialog' interface AdditionalInitializersProps { items: AdditionalInitializerSetting[] registeredInitializers: RegisteredInitializer[] + catalogUnavailable?: boolean creating: boolean savingInitializerId?: string | null saveErrors?: Record @@ -157,6 +158,7 @@ function AdditionalInitializerCard({ export default function AdditionalInitializers({ items, registeredInitializers, + catalogUnavailable, creating, savingInitializerId = null, saveErrors = {}, @@ -237,7 +239,11 @@ export default function AdditionalInitializers({ {items.map((item: BaselineInitializerSetting) => { - const initializer = resolveRegisteredInitializer(item.initializer_name, registeredInitializers) + const initializer = resolveRegisteredInitializer( + item.initializer_name, + registeredInitializers, + catalogUnavailable, + ) return (
(EMPTY_SETTINGS) const [registeredInitializers, setRegisteredInitializers] = useState([]) + const [catalogUnavailable, setCatalogUnavailable] = useState(false) const [loading, setLoading] = useState(true) const [statusMessage, setStatusMessage] = useState(null) const [refetchCount, setRefetchCount] = useState(0) @@ -55,7 +56,9 @@ export default function Initializers() { if (registeredResult.status === 'fulfilled') { setRegisteredInitializers(registeredResult.value.items) + setCatalogUnavailable(false) } else { + setCatalogUnavailable(true) const catalogError = toApiError(registeredResult.reason).detail setStatusMessage((current: StatusMessage | null) => current @@ -207,10 +210,12 @@ export default function Initializers() { { expect(result.initializer_name).toBe('target') expect(result.initializer_type).toBe('UnknownInitializer') }) + + it('returns a "temporarily unavailable" placeholder when the catalog fetch failed', () => { + const result = resolveRegisteredInitializer('target', [], true) + + expect(result).toEqual({ + initializer_name: 'target', + initializer_type: 'UnknownInitializer', + description: 'Catalog metadata temporarily unavailable.', + required_env_vars: [], + supported_parameters: [], + }) + }) + + it('still matches by name when the catalog is available', () => { + const result = resolveRegisteredInitializer('target', registered, false) + + expect(result).toBe(registered[0]) + }) }) diff --git a/frontend/src/components/Initializers/initializerLookup.ts b/frontend/src/components/Initializers/initializerLookup.ts index 73b3d889b1..94d0ff10bd 100644 --- a/frontend/src/components/Initializers/initializerLookup.ts +++ b/frontend/src/components/Initializers/initializerLookup.ts @@ -10,12 +10,23 @@ import type { RegisteredInitializer } from '@/types' export function resolveRegisteredInitializer( initializerName: string, registeredInitializers: RegisteredInitializer[], + catalogUnavailable?: boolean, ): RegisteredInitializer { const match = registeredInitializers.find((item) => item.initializer_name === initializerName) if (match) { return match } + if (catalogUnavailable) { + return { + initializer_name: initializerName, + initializer_type: 'UnknownInitializer', + description: 'Catalog metadata temporarily unavailable.', + required_env_vars: [], + supported_parameters: [], + } + } + return { initializer_name: initializerName, initializer_type: 'UnknownInitializer', From 5796799e771a06ad1ea308b1482eb066e02bdf1e Mon Sep 17 00:00:00 2001 From: fei <204683769+feiiiiii5@users.noreply.github.com> Date: Tue, 25 Aug 2026 14:03:30 +0800 Subject: [PATCH 2/5] FIX: disable Edit on initializer cards while the catalog is unavailable When the listRegistered request fails, each card renders the catalog-unavailable placeholder with no parameter schema, but Edit stayed enabled. Saving from that state replaced the stored parameters (such as { mode: 'strict' }) with null. Edit is now disabled while catalogUnavailable is true, and the card states that editing resumes once the catalog loads. Apply and Remove stay available because they use the stored parameters directly. Signed-off-by: fei <204683769+feiiiiii5@users.noreply.github.com> --- .../AdditionalInitializers.test.tsx | 21 +++++++++++++++++++ .../Initializers/AdditionalInitializers.tsx | 10 ++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Initializers/AdditionalInitializers.test.tsx b/frontend/src/components/Initializers/AdditionalInitializers.test.tsx index 74a909d626..d5b29ec013 100644 --- a/frontend/src/components/Initializers/AdditionalInitializers.test.tsx +++ b/frontend/src/components/Initializers/AdditionalInitializers.test.tsx @@ -174,6 +174,27 @@ describe('AdditionalInitializers', () => { expect(within(row).queryByRole('textbox', { name: 'Parameters JSON' })).not.toBeInTheDocument() }) + it('should disable Edit while the initializer catalog is unavailable', () => { + render( + + + , + ) + + const row = screen.getByTestId('initializer-row-additional-1') + const editButton = within(row).getByRole('button', { name: 'Edit' }) + expect(editButton).toBeDisabled() + expect( + within(row).getByText('Initializer catalog is unavailable. Edit is disabled until the catalog loads.'), + ).toBeInTheDocument() + // The saved parameters stay visible so operators can still see the stored value. + expect(within(row).getByText(/"tags"/)).toBeInTheDocument() + }) + it('should show the description as hover text on the initializer name', async () => { const user = userEvent.setup() diff --git a/frontend/src/components/Initializers/AdditionalInitializers.tsx b/frontend/src/components/Initializers/AdditionalInitializers.tsx index d3a278218b..b7e0f32027 100644 --- a/frontend/src/components/Initializers/AdditionalInitializers.tsx +++ b/frontend/src/components/Initializers/AdditionalInitializers.tsx @@ -41,6 +41,7 @@ interface AdditionalInitializersProps { interface AdditionalInitializerCardProps { item: AdditionalInitializerSetting initializer: RegisteredInitializer + catalogUnavailable?: boolean isSaving: boolean isApplying: boolean isDeleting: boolean @@ -54,6 +55,7 @@ interface AdditionalInitializerCardProps { function AdditionalInitializerCard({ item, initializer, + catalogUnavailable, isSaving, isApplying, isDeleting, @@ -94,6 +96,11 @@ function AdditionalInitializerCard({ Required env vars: {initializer.required_env_vars.join(', ')} )} + {catalogUnavailable && ( + + Initializer catalog is unavailable. Edit is disabled until the catalog loads. + + )}
@@ -111,7 +118,7 @@ function AdditionalInitializerCard({
-