Skip to content

Commit 4671b7b

Browse files
cvxluoclaude
andauthored
fix(settings): Surface slug validation errors on org settings form (#112556)
When changing the org slug to one already in use, the backend returns a field-level error but the frontend was showing a generic 'Unable to save change' toast. This was a regression from the form migration to TanStack which landed after the backend fix. Use setFieldErrors to display the API validation message inline on the slug field instead of swallowing it. <img width="1196" height="260" alt="Screenshot 2026-04-08 at 6 00 26 PM" src="https://github.com/user-attachments/assets/77af01f0-f9b9-4fca-ab64-5754be786036" /> I kept the toast showing up so this diff remains small. Fixes #112376 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 49d44f8 commit 4671b7b

File tree

2 files changed

+30
-2
lines changed

2 files changed

+30
-2
lines changed

static/app/views/settings/organizationGeneralSettings/organizationSettingsForm.spec.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,28 @@ describe('OrganizationSettingsForm', () => {
156156
expect(screen.queryByRole('button', {name: 'Cancel'})).not.toBeInTheDocument();
157157
});
158158

159+
it('shows field error when slug is already taken', async () => {
160+
MockApiClient.addMockResponse({
161+
url: `/organizations/${organization.slug}/`,
162+
method: 'PUT',
163+
statusCode: 400,
164+
body: {slug: ['The slug "taken" is in use by another organization.']},
165+
});
166+
167+
render(
168+
<OrganizationSettingsForm initialData={OrganizationFixture()} onSave={onSave} />
169+
);
170+
171+
const input = screen.getByRole('textbox', {name: 'Organization Slug'});
172+
await userEvent.clear(input);
173+
await userEvent.type(input, 'taken');
174+
await userEvent.click(screen.getByRole('button', {name: 'Save'}));
175+
176+
expect(
177+
await screen.findByText('The slug "taken" is in use by another organization.')
178+
).toBeInTheDocument();
179+
});
180+
159181
it('can enable codecov', async () => {
160182
putMock = MockApiClient.addMockResponse({
161183
url: `/organizations/${organization.slug}/`,

static/app/views/settings/organizationGeneralSettings/organizationSettingsForm.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
defaultFormOptions,
1212
FieldGroup,
1313
FormSearch,
14+
setFieldErrors,
1415
useScrapsForm,
1516
} from '@sentry/scraps/form';
1617
import {Container, Flex} from '@sentry/scraps/layout';
@@ -29,6 +30,7 @@ import {ConfigStore} from 'sentry/stores/configStore';
2930
import type {MembershipSettingsProps} from 'sentry/types/hooks';
3031
import type {Organization} from 'sentry/types/organization';
3132
import {fetchMutation, useMutation} from 'sentry/utils/queryClient';
33+
import {RequestError} from 'sentry/utils/requestError/requestError';
3234
import {slugify} from 'sentry/utils/slugify';
3335
import {useMembers} from 'sentry/utils/useMembers';
3436
import {useOrganization} from 'sentry/utils/useOrganization';
@@ -437,10 +439,14 @@ export function OrganizationSettingsForm({initialData, onSave}: Props) {
437439
...defaultFormOptions,
438440
defaultValues: {slug: initialData.slug},
439441
validators: {onDynamic: slugSchema},
440-
onSubmit: ({value}) =>
442+
onSubmit: ({value, formApi}) =>
441443
updateSlug({slug: value.slug})
442444
.then(() => slugForm.reset())
443-
.catch(() => {}),
445+
.catch(error => {
446+
if (error instanceof RequestError) {
447+
setFieldErrors(formApi, error);
448+
}
449+
}),
444450
});
445451

446452
return (

0 commit comments

Comments
 (0)