Error Handling
Proper error handling is crucial when working with phone numbers. This guide covers the exceptions you’ll encounter and best practices for handling them gracefully.NumberParseException
The primary exception you’ll encounter isNumberParseException, thrown when parsing fails:
use libphonenumber\PhoneNumberUtil;
use libphonenumber\NumberParseException;
$phoneUtil = PhoneNumberUtil::getInstance();
try {
$number = $phoneUtil->parse('invalid', 'US');
} catch (NumberParseException $e) {
echo "Error Type: " . $e->getErrorType();
echo "Message: " . $e->getMessage();
}
Error Types
TheNumberParseException provides five error type constants:
- INVALID_COUNTRY_CODE
- NOT_A_NUMBER
- TOO_SHORT_AFTER_IDD
- TOO_SHORT_NSN
- TOO_LONG
Error Code:
0The country code doesn’t belong to a supported country:try {
// Invalid country code +9999
$number = $phoneUtil->parse('+9999 123 4567', null);
} catch (NumberParseException $e) {
if ($e->getErrorType() === NumberParseException::INVALID_COUNTRY_CODE) {
echo "The country code is not recognized";
}
}
Error Code:
1The string doesn’t contain a valid phone number:try {
// Not enough digits
$number = $phoneUtil->parse('12', 'US');
} catch (NumberParseException $e) {
if ($e->getErrorType() === NumberParseException::NOT_A_NUMBER) {
echo "This is not a valid phone number format";
}
}
This error means the string had less than 3 digits or failed basic validation patterns.
Error Code:
2After removing the international dialing prefix, the number is too short:try {
// Has IDD prefix but number is too short
$number = $phoneUtil->parse('011 44 123', 'US');
} catch (NumberParseException $e) {
if ($e->getErrorType() === NumberParseException::TOO_SHORT_AFTER_IDD) {
echo "Number is too short after removing country calling code";
}
}
Error Code:
3The national significant number is too short:try {
// Number too short for US
$number = $phoneUtil->parse('123', 'US');
} catch (NumberParseException $e) {
if ($e->getErrorType() === NumberParseException::TOO_SHORT_NSN) {
echo "Phone number is too short";
}
}
Error Code:
4The number has more digits than any valid phone number:try {
// Way too many digits
$number = $phoneUtil->parse('12345678901234567890', 'US');
} catch (NumberParseException $e) {
if ($e->getErrorType() === NumberParseException::TOO_LONG) {
echo "Phone number is too long";
}
}
The maximum length for phone numbers is typically 17 digits.
User-Friendly Error Messages
Convert error types to user-friendly messages:function getErrorMessage(NumberParseException $e): string
{
return match($e->getErrorType()) {
NumberParseException::INVALID_COUNTRY_CODE =>
'The country code is not recognized. Please check the number.',
NumberParseException::NOT_A_NUMBER =>
'This doesn\'t appear to be a valid phone number.',
NumberParseException::TOO_SHORT_AFTER_IDD =>
'The phone number is too short for the country specified.',
NumberParseException::TOO_SHORT_NSN =>
'The phone number is too short. Please check and try again.',
NumberParseException::TOO_LONG =>
'The phone number is too long. Please check and try again.',
default =>
'Unable to parse the phone number. Please check the format.'
};
}
// Usage
try {
$number = $phoneUtil->parse($userInput, $region);
} catch (NumberParseException $e) {
echo getErrorMessage($e);
}
Complete Error Handling Pattern
Parse with Try-Catch
$phoneUtil = PhoneNumberUtil::getInstance();
try {
$number = $phoneUtil->parse($userInput, $region);
} catch (NumberParseException $e) {
// Handle parsing error
return [
'success' => false,
'error' => getErrorMessage($e),
'errorCode' => $e->getErrorType()
];
}
Validate the Parsed Number
if (!$phoneUtil->isValidNumber($number)) {
return [
'success' => false,
'error' => 'The phone number is not valid',
'errorCode' => 'INVALID_NUMBER'
];
}
Optional: Additional Validation
// Check if it's a mobile number (if required)
$type = $phoneUtil->getNumberType($number);
if ($type !== PhoneNumberType::MOBILE) {
return [
'success' => false,
'error' => 'Please provide a mobile phone number',
'errorCode' => 'NOT_MOBILE'
];
}
Practical Examples
- Form Validation
- API Response
- Batch Processing
class PhoneNumberValidator
{
private PhoneNumberUtil $phoneUtil;
public function __construct()
{
$this->phoneUtil = PhoneNumberUtil::getInstance();
}
public function validate(
string $input,
string $region
): array {
// Empty check
if (empty(trim($input))) {
return [
'valid' => false,
'error' => 'Phone number is required'
];
}
// Parse
try {
$number = $this->phoneUtil->parse($input, $region);
} catch (NumberParseException $e) {
return [
'valid' => false,
'error' => $this->getErrorMessage($e),
'errorType' => $e->getErrorType()
];
}
// Validate
if (!$this->phoneUtil->isValidNumber($number)) {
return [
'valid' => false,
'error' => 'This phone number is not valid for any region'
];
}
// Check if it's for the expected region (optional)
if (!$this->phoneUtil->isValidNumberForRegion($number, $region)) {
return [
'valid' => false,
'error' => "This doesn't appear to be a {$region} phone number",
'warning' => true // Soft error
];
}
return [
'valid' => true,
'e164' => $this->phoneUtil->format(
$number,
PhoneNumberFormat::E164
),
'international' => $this->phoneUtil->format(
$number,
PhoneNumberFormat::INTERNATIONAL
),
'type' => $this->phoneUtil->getNumberType($number),
'region' => $this->phoneUtil->getRegionCodeForNumber($number)
];
}
private function getErrorMessage(NumberParseException $e): string
{
return match($e->getErrorType()) {
NumberParseException::INVALID_COUNTRY_CODE =>
'Invalid country code',
NumberParseException::NOT_A_NUMBER =>
'Not a valid phone number format',
NumberParseException::TOO_SHORT_AFTER_IDD =>
'Number too short after country code',
NumberParseException::TOO_SHORT_NSN =>
'Phone number is too short',
NumberParseException::TOO_LONG =>
'Phone number is too long',
default =>
'Invalid phone number'
};
}
}
// Usage in controller
$validator = new PhoneNumberValidator();
$result = $validator->validate($_POST['phone'], 'US');
if (!$result['valid']) {
// Show error to user
echo json_encode([
'error' => $result['error']
]);
} else {
// Save to database
saveContact($result['e164']);
}
class PhoneNumberController
{
public function validatePhone(Request $request): JsonResponse
{
$phoneUtil = PhoneNumberUtil::getInstance();
$input = $request->input('phone');
$region = $request->input('region', 'US');
try {
$number = $phoneUtil->parse($input, $region);
if (!$phoneUtil->isValidNumber($number)) {
return response()->json([
'valid' => false,
'error' => 'INVALID_NUMBER',
'message' => 'The phone number is not valid'
], 400);
}
return response()->json([
'valid' => true,
'data' => [
'e164' => $phoneUtil->format(
$number,
PhoneNumberFormat::E164
),
'international' => $phoneUtil->format(
$number,
PhoneNumberFormat::INTERNATIONAL
),
'national' => $phoneUtil->format(
$number,
PhoneNumberFormat::NATIONAL
),
'region' => $phoneUtil->getRegionCodeForNumber($number),
'type' => $phoneUtil->getNumberType($number)
]
]);
} catch (NumberParseException $e) {
$errorCodes = [
NumberParseException::INVALID_COUNTRY_CODE => 'INVALID_COUNTRY_CODE',
NumberParseException::NOT_A_NUMBER => 'NOT_A_NUMBER',
NumberParseException::TOO_SHORT_AFTER_IDD => 'TOO_SHORT_AFTER_IDD',
NumberParseException::TOO_SHORT_NSN => 'TOO_SHORT',
NumberParseException::TOO_LONG => 'TOO_LONG'
];
return response()->json([
'valid' => false,
'error' => $errorCodes[$e->getErrorType()] ?? 'PARSE_ERROR',
'message' => $e->getMessage()
], 400);
}
}
}
function processBatch(array $phoneNumbers, string $region): array
{
$phoneUtil = PhoneNumberUtil::getInstance();
$results = [
'success' => [],
'errors' => []
];
foreach ($phoneNumbers as $id => $phoneNumber) {
try {
$number = $phoneUtil->parse($phoneNumber, $region);
if (!$phoneUtil->isValidNumber($number)) {
$results['errors'][$id] = [
'input' => $phoneNumber,
'error' => 'INVALID_NUMBER',
'message' => 'Not a valid phone number'
];
continue;
}
$results['success'][$id] = [
'input' => $phoneNumber,
'e164' => $phoneUtil->format(
$number,
PhoneNumberFormat::E164
),
'region' => $phoneUtil->getRegionCodeForNumber($number)
];
} catch (NumberParseException $e) {
$results['errors'][$id] = [
'input' => $phoneNumber,
'error' => 'PARSE_ERROR',
'errorType' => $e->getErrorType(),
'message' => $e->getMessage()
];
} catch (\Exception $e) {
$results['errors'][$id] = [
'input' => $phoneNumber,
'error' => 'UNEXPECTED_ERROR',
'message' => $e->getMessage()
];
}
}
return $results;
}
// Usage
$numbers = [
'user1' => '+1 650 253 0000',
'user2' => 'invalid',
'user3' => '+44 117 496 0123'
];
$results = processBatch($numbers, 'US');
echo "Processed: " . count($results['success']) . " successful\n";
echo "Failed: " . count($results['errors']) . " errors\n";
Logging Errors
Log errors for debugging and monitoring:use Psr\Log\LoggerInterface;
class PhoneNumberService
{
private PhoneNumberUtil $phoneUtil;
private LoggerInterface $logger;
public function __construct(LoggerInterface $logger)
{
$this->phoneUtil = PhoneNumberUtil::getInstance();
$this->logger = $logger;
}
public function parseAndValidate(string $input, string $region): ?PhoneNumber
{
try {
$number = $this->phoneUtil->parse($input, $region);
if (!$this->phoneUtil->isValidNumber($number)) {
$this->logger->warning('Invalid phone number', [
'input' => $input,
'region' => $region
]);
return null;
}
return $number;
} catch (NumberParseException $e) {
$this->logger->error('Phone number parse error', [
'input' => $input,
'region' => $region,
'errorType' => $e->getErrorType(),
'message' => $e->getMessage()
]);
return null;
} catch (\Exception $e) {
$this->logger->critical('Unexpected error parsing phone number', [
'input' => $input,
'region' => $region,
'exception' => $e
]);
return null;
}
}
}
Best Practices
Always Use Try-Catch
Never call parse() without a try-catch block in production code
Validate After Parsing
Successful parsing doesn’t guarantee a valid number - always validate
Provide Clear Messages
Convert error types to user-friendly messages
Log Errors
Log parsing errors for debugging and monitoring
Common Pitfalls
Avoid these common mistakes:
- Not catching
NumberParseException - Assuming parsing success means valid number
- Not checking for null/empty input before parsing
- Ignoring error types and showing generic messages
- Not logging errors for debugging
Testing Error Handling
Test your error handling with invalid inputs:use PHPUnit\Framework\TestCase;
class PhoneNumberValidatorTest extends TestCase
{
public function testInvalidCountryCode(): void
{
$validator = new PhoneNumberValidator();
$result = $validator->validate('+9999 123 4567', 'US');
$this->assertFalse($result['valid']);
$this->assertEquals(
NumberParseException::INVALID_COUNTRY_CODE,
$result['errorType']
);
}
public function testTooShort(): void
{
$validator = new PhoneNumberValidator();
$result = $validator->validate('123', 'US');
$this->assertFalse($result['valid']);
}
public function testTooLong(): void
{
$validator = new PhoneNumberValidator();
$result = $validator->validate('12345678901234567890', 'US');
$this->assertFalse($result['valid']);
}
public function testValidNumber(): void
{
$validator = new PhoneNumberValidator();
$result = $validator->validate('+1 650 253 0000', 'US');
$this->assertTrue($result['valid']);
$this->assertEquals('+16502530000', $result['e164']);
}
}
Next Steps
Parsing Numbers
Learn more about parsing phone numbers
Validating Numbers
Complete guide to validation
NumberParseException API
Complete API reference for exception handling
Best Practices
Review parsing best practices