The Quick Fix
You're seeing this error because your match expression encountered a value it wasn't programmed to handle. Unlike a switch statement, which silently ignores missing cases, a match expression must be exhaustive. It requires an outcome for every possible input.
The fastest solution is adding a default arm at the end of your block:
$result = match ($status) {
'active' => 'User is online',
'inactive' => 'User is offline',
default => 'Status unknown', // The safety net
};
Why This Error Happens
PHP 8.0 introduced match as a stricter, more modern alternative to switch. It doesn't just compare values; it expects certainty. If the value passed to match() doesn't exist in your defined list, PHP throws an UnhandledMatchError and halts execution.
This crash usually stems from two issues:
- **Missing Logic:** Your code received a new value (like a 'pending' status) that you haven't added to the match arms yet.
- **Type Mismatches:** The `match` expression uses strict identity checks (`===`). An integer `200` will not match a string `"200"`.
Here is a classic example of a crash caused by an unhandled HTTP code:
$httpStatus = 404;
$message = match ($httpStatus) {
200 => "Success",
500 => "Server Error",
};
// Result: Fatal error: Uncaught UnhandledMatchError: Unhandled match case 404
Solution 1: Use a Fallback Arm
Adding a default case is the most reliable way to prevent crashes. It acts as a catch-all for any unexpected input. Use this when you want your application to keep running even if it encounters data you didn't anticipate.
$role = 'manager';
$permission = match ($role) {
'admin' => 'full_access',
'editor' => 'edit_content',
default => 'read_only', // Safely handles 'manager', 'guest', or null
};
Solution 2: Enforce Strict Type Handling
Since match is type-sensitive, data coming from $_GET, $_POST, or a database often causes issues because those sources usually return strings. If you expect an integer, cast the variable before matching it. This prevents "1" from failing against the integer 1.
$userId = "101"; // A string from a URL parameter
$group = match ((int)$userId) {
101 => "Admin Group",
102 => "Staff Group",
default => "General Users",
};
Solution 3: Recovering with Try-Catch
In rare cases, you might want to treat a missing case as a recoverable event rather than a logic error. You can wrap the expression in a try-catch block to log the issue without breaking the entire page load.
try {
$color = match ($input) {
'red' => '#FF0000',
'blue' => '#0000FF',
};
} catch (\UnhandledMatchError $e) {
$color = '#FFFFFF'; // Default to white
error_log("Warning: User provided unsupported color: " . $input);
}
Solution 4: The Best Practice (PHP 8.1+ Enums)
If you are on PHP 8.1 or newer, Enums are the gold standard. By using an Enum as your input type, you restrict the possible values at the language level. Modern IDEs and static analysis tools like PHPStan will even highlight your code in red if you forget to handle one of the Enum cases.
enum UserStatus {
case Active;
case Banned;
case Deleted;
}
function getStatusMessage(UserStatus $status): string {
return match ($status) {
UserStatus::Active => 'Welcome back!',
UserStatus::Banned => 'Access denied.',
UserStatus::Deleted => 'Account no longer exists.',
// No default needed because all Enum cases are covered
};
}
Verifying the Fix
Test your logic by passing an invalid value through your match block. You can use a simple CLI script to confirm the default arm catches the error as expected.
<?php
// test_match.php
function checkMatch($val) {
return match ($val) {
1 => "Found One",
default => "Caught unexpected value: " . $val,
};
}
echo checkMatch(1) . PHP_EOL; // Output: Found One
echo checkMatch(999) . PHP_EOL; // Output: Caught unexpected value: 999
Run php test_match.php in your terminal. If the script prints the "Caught" message instead of throwing a Fatal Error, your fix is solid.
Key Takeaways
Switching from switch to match requires a mindset shift regarding strictness:
- **Switch:** Uses loose comparison (`==`) and ignores missing cases.
- **Match:** Uses strict identity (`===`) and crashes on missing cases.

