Skip to main content

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 is NumberParseException, 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

The NumberParseException provides five error type constants:
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";
    }
}

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

1

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()
    ];
}
2

Validate the Parsed Number

if (!$phoneUtil->isValidNumber($number)) {
    return [
        'success' => false,
        'error' => 'The phone number is not valid',
        'errorCode' => 'INVALID_NUMBER'
    ];
}
3

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'
    ];
}
4

Return Success

return [
    'success' => true,
    'number' => $phoneUtil->format(
        $number,
        PhoneNumberFormat::E164
    ),
    'formatted' => $phoneUtil->format(
        $number,
        PhoneNumberFormat::INTERNATIONAL
    )
];

Practical Examples

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']);
}

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

Build docs developers (and LLMs) love