diff --git a/frontend/src/components/Initializers/AdditionalInitializers.test.tsx b/frontend/src/components/Initializers/AdditionalInitializers.test.tsx
index d0d0438436..c05d401cd8 100644
--- a/frontend/src/components/Initializers/AdditionalInitializers.test.tsx
+++ b/frontend/src/components/Initializers/AdditionalInitializers.test.tsx
@@ -365,4 +365,35 @@ describe('AdditionalInitializers', () => {
expect(defaultProps.onAdd).toHaveBeenCalledWith('load_default_datasets', null)
})
+
+ it('should show a server error inside the add dialog when onAdd fails', async () => {
+ const user = userEvent.setup()
+
+ const props = {
+ ...defaultProps,
+ registeredInitializers: [refreshInitializer],
+ onAdd: jest.fn().mockRejectedValue(new Error('Invalid days value.')),
+ }
+
+ render(
+
+
+ ,
+ )
+
+ fireEvent.change(screen.getByRole('combobox', { name: 'Initializer to add' }), {
+ target: { value: 'refresh_datasets' },
+ })
+ await user.click(screen.getByRole('button', { name: 'Add initializer' }))
+
+ const dialog = await screen.findByRole('dialog', {}, { timeout: 3000 })
+ await within(dialog).findByText('Add refresh_datasets initializer')
+ fireEvent.change(within(dialog).getByTestId('param-days'), { target: { value: '12' } })
+ await user.click(await within(dialog).findByRole('button', { name: 'Add', hidden: true }))
+
+ expect(await within(dialog).findByRole('alert', { hidden: true })).toHaveTextContent(
+ 'Invalid days value.',
+ )
+ expect(dialog).toBeInTheDocument()
+ })
})
diff --git a/frontend/src/components/Initializers/AdditionalInitializers.tsx b/frontend/src/components/Initializers/AdditionalInitializers.tsx
index 9ad4a8c06a..e415eba4a7 100644
--- a/frontend/src/components/Initializers/AdditionalInitializers.tsx
+++ b/frontend/src/components/Initializers/AdditionalInitializers.tsx
@@ -14,6 +14,7 @@ import type {
UpdateAdditionalInitializerRequest,
} from '@/types'
+import { toApiError } from '@/services/errors'
import { useAdditionalInitializersStyles } from './AdditionalInitializers.styles'
import { formatInitializerParameters, formatSupportedParameterSummary } from './initializerFormatting'
import { resolveRegisteredInitializer } from './initializerLookup'
@@ -153,6 +154,7 @@ export default function AdditionalInitializers({
const listStyles = useAdditionalInitializersStyles()
const [selectedInitializerName, setSelectedInitializerName] = useState('')
const [addDialogOpen, setAddDialogOpen] = useState(false)
+ const [addError, setAddError] = useState(null)
const initializerName = selectedInitializerName || registeredInitializers[0]?.initializer_name || ''
const selectedInitializer = registeredInitializers.find(
(initializer) => initializer.initializer_name === initializerName,
@@ -162,9 +164,14 @@ export default function AdditionalInitializers({
if (!initializerName) {
return
}
- const added = await onAdd(initializerName, parameters)
- if (added) {
- setAddDialogOpen(false)
+ setAddError(null)
+ try {
+ const added = await onAdd(initializerName, parameters)
+ if (added) {
+ setAddDialogOpen(false)
+ }
+ } catch (e) {
+ setAddError(toApiError(e).detail)
}
}
@@ -231,8 +238,14 @@ export default function AdditionalInitializers({
initializer={selectedInitializer}
initialParameters={null}
submitting={creating}
+ externalError={addError}
onSubmit={handleAdd}
- onOpenChange={setAddDialogOpen}
+ onOpenChange={(open) => {
+ setAddDialogOpen(open)
+ if (!open) {
+ setAddError(null)
+ }
+ }}
/>
)}
diff --git a/frontend/src/components/Initializers/InitializerParametersDialog.test.tsx b/frontend/src/components/Initializers/InitializerParametersDialog.test.tsx
index d0fda42461..5fa0d84c73 100644
--- a/frontend/src/components/Initializers/InitializerParametersDialog.test.tsx
+++ b/frontend/src/components/Initializers/InitializerParametersDialog.test.tsx
@@ -207,4 +207,18 @@ describe('InitializerParametersDialog', () => {
expect(screen.getByRole('button', { name: 'Add...' })).toBeDisabled()
expect(screen.getByRole('button', { name: 'Cancel' })).toBeDisabled()
})
+
+ it('displays an external error passed via externalError prop', () => {
+ render(
+
+
+ ,
+ )
+
+ expect(screen.getByRole('alert')).toHaveTextContent('Server rejected the request.')
+ })
})
diff --git a/frontend/src/components/Initializers/InitializerParametersDialog.tsx b/frontend/src/components/Initializers/InitializerParametersDialog.tsx
index 44e8ba832d..1d2c56dd8f 100644
--- a/frontend/src/components/Initializers/InitializerParametersDialog.tsx
+++ b/frontend/src/components/Initializers/InitializerParametersDialog.tsx
@@ -31,6 +31,7 @@ interface InitializerParametersDialogProps {
initializer: RegisteredInitializer | null
initialParameters?: Record | null
submitting?: boolean
+ externalError?: string | null
onSubmit: (parameters: Record | null) => void | Promise
onOpenChange: (open: boolean) => void
}
@@ -41,6 +42,7 @@ export default function InitializerParametersDialog({
initializer,
initialParameters = null,
submitting = false,
+ externalError = null,
onSubmit,
onOpenChange,
}: InitializerParametersDialogProps) {
@@ -112,9 +114,9 @@ export default function InitializerParametersDialog({
This initializer takes no parameters.
)}
- {error && (
+ {(error || externalError) && (
- {error}
+ {error || externalError}
)}
diff --git a/frontend/src/components/Initializers/Initializers.tsx b/frontend/src/components/Initializers/Initializers.tsx
index 684f085675..6b2c6f2f4b 100644
--- a/frontend/src/components/Initializers/Initializers.tsx
+++ b/frontend/src/components/Initializers/Initializers.tsx
@@ -94,8 +94,9 @@ export default function Initializers() {
await refetchSettingsOnly()
return true
} catch (error) {
- setStatusMessage({ intent: 'error', text: toApiError(error).detail })
- return false
+ const detail = toApiError(error).detail
+ setStatusMessage({ intent: 'error', text: detail })
+ throw error
} finally {
setCreating(false)
}