Regular expressions7 min readExamples run on PHP 8.5.10

preg_last_error_msg()

Learn how preg_last_error_msg() turns the status of the last PCRE call into a readable message, with examples, edge cases and common mistakes.

Signature

preg_last_error_msg(): string

Purpose

PHP's preg_* functions signal many runtime problems by returning false or null rather than throwing an exception. A false result alone does not say why the call failed. preg_last_error_msg() fills that gap: it returns a human-readable description of the status left by the most recent PCRE call. It is the text counterpart of preg_last_error(), which returns an integer code.

Use it when you log failures, build diagnostics, or wrap regex calls in code that must report what went wrong.

Signature

The function is documented as available from PHP 8.

Parameters and defaults

There are no parameters, so there are no defaults. The function always reports on the last PCRE function call in the current request.

Return value and failure cases

The function always returns a string. If the last PCRE call finished without a problem, the string is No error. Otherwise it describes the problem. Typical messages include:

  • Internal error
  • Backtrack limit exhausted
  • Recursion limit exhausted
  • Malformed UTF-8 characters, possibly incorrectly encoded
  • JIT stack limit exhausted

The function never throws and never returns false. The failure being reported belongs to the earlier preg_* call, not to preg_last_error_msg() itself.

Example 1: a successful match

After a call that completes normally, the message is No error. This includes a call that simply finds no match, because not matching is not an error.

Example
<?php
preg_match('/\d+/', 'order 42');
echo preg_last_error_msg(), "\n";
Output
No error

Example 2: invalid UTF-8 input

With the u modifier, the subject must be valid UTF-8. The bytes C3 28 are not, so the match fails and returns false. The message tells you the reason.

Example
<?php
$result = preg_match('/./u', "\xC3\x28");
echo json_encode([$result, preg_last_error_msg()], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
[false,"Malformed UTF-8 characters, possibly incorrectly encoded"]

Example 3: a small wrapper that reports status

A wrapper keeps the result and the message together. It reads the message immediately after the call, before any other regex function can overwrite it.

Example
<?php
function tryMatch(string $pattern, string $subject): array
{
    $result = preg_match($pattern, $subject);
    return ['result' => $result, 'message' => preg_last_error_msg()];
}

echo json_encode(tryMatch('/./u', "\xC3\x28"), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
echo json_encode(tryMatch('/./u', 'ok'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"result":false,"message":"Malformed UTF-8 characters, possibly incorrectly encoded"}
{"result":1,"message":"No error"}

The second call shows that the status is replaced by each new PCRE call: the earlier UTF-8 failure does not linger.

Edge cases

  • Only the latest call counts. Each preg_* call replaces the stored status. If you run two regex calls and then read the message, you learn only about the second one.
  • No match is not an error. preg_match() returning 0 still leaves No error.
  • Resource limits depend on configuration. The manual's example uses a pattern that backtracks heavily and reports Backtrack limit exhausted. Whether a given pattern hits a limit can depend on PCRE settings such as pcre.backtrack_limit and on whether the JIT is active, so do not rely on one pattern to trigger a specific message across servers.
  • Compile problems differ. A pattern that cannot be compiled is a different kind of failure from a runtime limit, and PHP may also raise a warning for it. Treat the message as one source of information, not the only one.
  • Calling it before any regex call. If no PCRE function has run, there is no error to report, so expect No error.

Common mistakes

  1. Checking the message instead of the return value. A string such as No error does not prove your pattern matched. Check the return value of preg_match() first, using strict comparison with false, and consult the message only when a failure is indicated.
  2. Reading the message too late. Calling another preg_* function, or code that calls one internally, before reading the status overwrites it.
  3. Using == false on preg_match(). Because 0 also equals false loosely, a plain no-match gets treated like a failure. Use === false.
  4. Parsing the message text. The strings are meant for people. If your code needs to branch on the kind of failure, compare the integer from preg_last_error() with the PREG_*_ERROR constants.
  5. Assuming it clears itself on demand. You cannot reset the status directly; it only changes when another PCRE call runs.
  • preg_last_error() returns the numeric code for the same status.
  • preg_match(), preg_match_all(), preg_replace() and preg_split() are the calls whose status it reports.
  • json_last_error_msg() follows the same pattern for the JSON functions.

Recap

preg_last_error_msg() takes no arguments and returns a string describing the last PCRE call. No error means that call completed normally, even with no match. Check the return value of the regex function first, read the message right away, and use preg_last_error() when code must branch on a specific failure.

Tested examples

Successful match leaves No error
<?php
preg_match('/\d+/', 'order 42');
echo preg_last_error_msg(), "\n";
Output
No error
Invalid UTF-8 input with the u modifier
<?php
$result = preg_match('/./u', "\xC3\x28");
echo json_encode([$result, preg_last_error_msg()], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
[false,"Malformed UTF-8 characters, possibly incorrectly encoded"]
A wrapper that reports result and message
<?php
function tryMatch(string $pattern, string $subject): array
{
    $result = preg_match($pattern, $subject);
    return ['result' => $result, 'message' => preg_last_error_msg()];
}

echo json_encode(tryMatch('/./u', "\xC3\x28"), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
echo json_encode(tryMatch('/./u', 'ok'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"result":false,"message":"Malformed UTF-8 characters, possibly incorrectly encoded"}
{"result":1,"message":"No error"}