fix(front): guard board field updates against missing definitions

https://sonarly.com/issue/39344?type=bug

An unhandled frontend TypeError crashes the People object page when a user hides or reorders fields in a Kanban/custom view. The crash is caused by writing into an array entry that does not exist.

Fix: I implemented a defensive fix in the board options hook to prevent undefined writes when `findIndex()` returns `-1` during transient state desynchronization.

Specifically, in `useObjectOptionsForBoard` I updated both mutation paths:
- reorder path (`position` update)
- visibility path (`isVisible` update)

Each path now resolves the target entry first and exits the draft mutation when the entry is missing, instead of writing directly to `draftRecordIndexFieldDefinitions[indexToModify]`. This prevents the unhandled `TypeError` (`setting 'position'` / `setting 'isVisible'`) and keeps the session usable while state converges.

Authored by Sonarly by autonomous analysis (run 44810).
This commit is contained in:
sonarly-bot
2026-05-21 09:25:43 +00:00
parent 1ed347bc17
commit 3639426c0d
3 changed files with 47 additions and 14 deletions
@@ -50,19 +50,38 @@ export const PromiseRejectionEffect = () => {
);
}
try {
const { captureException } = await import('@sentry/react');
captureException(error, (scope) => {
scope.setExtras({ mechanism: 'onUnhandle' });
if (isAbortError) {
return;
}
const fingerprint = hasErrorCode(error) ? error.code : error.message;
scope.setFingerprint([fingerprint]);
error.name = error.message;
return scope;
try {
const { captureException, captureMessage } = await import('@sentry/react');
if (error instanceof Error) {
captureException(error, (scope) => {
scope.setExtras({ mechanism: 'onUnhandledRejection' });
const fingerprint = hasErrorCode(error)
? error.code
: error.message;
scope.setFingerprint([fingerprint]);
return scope;
});
return;
}
captureMessage('Unhandled promise rejection with non-error reason', {
level: 'warning',
extra: {
mechanism: 'onUnhandledRejection',
reasonType: typeof error,
},
});
} catch (sentryError) {
// oxlint-disable-next-line no-console
console.error('Failed to capture exception with Sentry:', sentryError);
console.warn('Failed to capture exception with Sentry:', sentryError);
}
},
[enqueueErrorSnackBar],
@@ -59,7 +59,7 @@ export const SentryInitEffect = () => {
setIsSentryInitialized(true);
} catch (error) {
// oxlint-disable-next-line no-console
console.error('Failed to initialize Sentry:', error);
console.warn('Failed to initialize Sentry:', error);
} finally {
setIsSentryInitializing(false);
}
@@ -83,7 +83,7 @@ export const SentryInitEffect = () => {
setIsSentryUserDefined(true);
} catch (error) {
// oxlint-disable-next-line no-console
console.error('Failed to set Sentry user:', error);
console.warn('Failed to set Sentry user:', error);
}
} else if (!isDefined(currentUser) && isSentryInitialized) {
try {
@@ -91,7 +91,7 @@ export const SentryInitEffect = () => {
setUser(null);
} catch (error) {
// oxlint-disable-next-line no-console
console.error('Failed to clear Sentry user:', error);
console.warn('Failed to clear Sentry user:', error);
}
}
};
@@ -114,7 +114,14 @@ export const useObjectOptionsForBoard = ({
updatedRecordField.fieldMetadataItemId,
);
draftRecordIndexFieldDefinitions[indexToModify].position =
const recordIndexFieldDefinitionToModify =
draftRecordIndexFieldDefinitions[indexToModify];
if (!isDefined(recordIndexFieldDefinitionToModify)) {
return;
}
recordIndexFieldDefinitionToModify.position =
updatedRecordField.position;
},
);
@@ -222,7 +229,14 @@ export const useObjectOptionsForBoard = ({
updatedRecordField.fieldMetadataItemId,
);
draftRecordIndexFieldDefinitions[indexToModify].isVisible =
const recordIndexFieldDefinitionToModify =
draftRecordIndexFieldDefinitions[indexToModify];
if (!isDefined(recordIndexFieldDefinitionToModify)) {
return;
}
recordIndexFieldDefinitionToModify.isVisible =
shouldShowFieldMetadataItem;
},
);