Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 4d26417e80 Phone field crashes on Enter: parsePhoneNumber throws instead of returning undefined
https://sonarly.com/issue/2690?type=bug

Editing a phone number on a company record and pressing Enter causes an unhandled ParseError('INVALID_COUNTRY') because the code uses the throwing variant of libphonenumber-js's parsePhoneNumber without a try-catch.

Fix: Two changes to fix the unhandled `ParseError('INVALID_COUNTRY')`:

**1. `PhonesFieldInput.tsx` — Use the non-throwing parser (primary fix)**

Changed the import from `parsePhoneNumber` (named export = `parsePhoneNumberWithError`, which **throws**) to `parsePhoneNumberFromString` (which returns `undefined` on failure). This matches the existing code's expectation at line 175 (`if (phone !== undefined)`), which was never reachable for invalid inputs because the throw happened first.

```diff
- import { parsePhoneNumber, type E164Number } from 'libphonenumber-js';
+ import { parsePhoneNumberFromString, type E164Number } from 'libphonenumber-js';
```

And at the call site:
```diff
- const phone = parsePhoneNumber(input);
+ const phone = parsePhoneNumberFromString(input);
```

**2. `MultiItemFieldInput.tsx` — Validate before formatting (defense-in-depth)**

Reordered `validateInputAndComputeUpdatedItems()` so that `validateInput()` runs **before** `formatInput()`. Previously, `formatInput` was called first (line 260), meaning any throwing formatter would bypass validation entirely. Now, invalid input is rejected by `phoneSchema.safeParse()` (which correctly uses try-catch) before `formatInput` is ever called.

Both other files using `parsePhoneNumber` (`PhonesDisplay.tsx`, `PhoneDisplay.tsx`) already wrap calls in try-catch, so no changes are needed there.
2026-03-06 15:31:59 +00:00
2 changed files with 9 additions and 9 deletions
@@ -257,13 +257,6 @@ export const MultiItemFieldInput = <T,>({
};
}
const newItem = formatInput
? formatInput(
sanitizedInput,
isAddingNewItem ? undefined : itemToEditIndex,
)
: (sanitizedInput as unknown as T);
if (validateInput !== undefined) {
const validationData = validateInput(sanitizedInput) ?? { isValid: true };
if (!validationData.isValid) {
@@ -273,6 +266,13 @@ export const MultiItemFieldInput = <T,>({
}
}
const newItem = formatInput
? formatInput(
sanitizedInput,
isAddingNewItem ? undefined : itemToEditIndex,
)
: (sanitizedInput as unknown as T);
return {
isValid: true,
updatedItems: isAddingNewItem
@@ -5,7 +5,7 @@ import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/re
import { phoneSchema } from '@/object-record/record-field/ui/validation-schemas/phoneSchema';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { styled } from '@linaria/react';
import { parsePhoneNumber, type E164Number } from 'libphonenumber-js';
import { parsePhoneNumberFromString, type E164Number } from 'libphonenumber-js';
import ReactPhoneNumberInput from 'react-phone-number-input';
import 'react-phone-number-input/style.css';
@@ -171,7 +171,7 @@ export const PhonesFieldInput = () => {
};
}
const phone = parsePhoneNumber(input);
const phone = parsePhoneNumberFromString(input);
if (phone !== undefined) {
return {
number: phone.nationalNumber,