mongodb/laravel-mongodb · error · LogicException
Missing expected ending delimiter
Error message
Missing expected ending delimiter "%s" in regular expression "%s"
What it means
After validating the leading delimiter of a regex where value, the pattern is exploded by the delimiter; fewer than 3 parts means the closing delimiter is missing (e.g. '/^abc'). The library needs the trailing delimiter to separate the pattern body from its flags and throws LogicException otherwise.
Solutions
- Close the pattern: ->where('name', 'regex', '/^abc/').
- Include flags after the closing delimiter if needed: '/^abc/i'.
- Prefer BSON Regex objects: new Regex('^abc', 'i') — no delimiter parsing at all.
- Escape inner occurrences of the delimiter (\/ for '/') so the count stays balanced.
Example fix
// before
$query->where('name', 'regex', '/^abc');
// after
$query->where('name', 'regex', '/^abc/i'); Defensive patterns
Strategy: validation
Validate before calling
if (is_string($pattern) && substr_count($pattern, substr($pattern, 0, 1)) < 2) {
$pattern .= substr($pattern, 0, 1); // close the delimiter
} Prevention
- Verify the pattern ends with its opening delimiter.
- Prefer new Regex($body, $flags) to skip parsing.
- Escape embedded delimiters in dynamic patterns.
When it happens
Trigger: ->where('name', 'regex', '/^abc') (no closing /); unbalanced delimiters where an escaped delimiter broke the count; patterns like '/ab/' missing the flags segment count due to typos.
Common situations: Dynamically-built patterns where the closing delimiter was dropped in string interpolation; hand-written patterns from Mongo shell that omit trailing slash.
Related errors
- Missing expected starting delimiter in regular expression
- Between $values must be a list with exactly two elements…
- 2nd argument of () must be "null" when 1st argument is an…
- Distinct queries cannot be used for pagination. Use GroupBy…
- The value used as a document id or relation key cannot…
AI-assisted analysis of mongodb/laravel-mongodb@0634653039 (2026-09-15).
Data as JSON: /api/errors/37498801c0a9e86e.
Report an issue: GitHub.
Appendix: source
Thrown at src/Query/Builder.php:1516
// For inverse like operations, we can just use the $not operator with the Regex
$operator = $operator === 'like' ? '=' : 'not';
// phpcs:ignore Squiz.ControlStructures.ControlSignature.SpaceAfterCloseBrace
}
// Manipulate regex operations.
elseif (in_array($operator, ['regex', 'not regex'])) {
// Automatically convert regular expression strings to Regex objects.
if (is_string($value)) {
// Detect the delimiter and validate the preg pattern
$delimiter = substr($value, 0, 1);
if (! in_array($delimiter, self::REGEX_DELIMITERS)) {
throw new LogicException(sprintf('Missing expected starting delimiter in regular expression "%s", supported delimiters are: %s', $value, implode(' ', self::REGEX_DELIMITERS)));
}
$e = explode($delimiter, $value);
// We don't try to detect if the last delimiter is escaped. This would be an invalid regex.
if (count($e) < 3) {
throw new LogicException(sprintf('Missing expected ending delimiter "%s" in regular expression "%s"', $delimiter, $value));
}
// Flags are after the last delimiter
$flags = end($e);
// Extract the regex string between the delimiters
$regstr = substr($value, 1, -1 - strlen($flags));
$value = new Regex($regstr, $flags);
}
// For inverse regex operations, we can just use the $not operator with the Regex
$operator = $operator === 'regex' ? '=' : 'not';
}
if (! isset($operator) || $operator === '=' || $operator === 'eq') {
$query = [$column => $value];
} else {
$query = [$column => ['$' . $operator => $value]];
}View on GitHub (pinned to 0634653039)