mongodb/laravel-mongodb · error · LogicException

Missing expected starting delimiter in regular expression

Error message

Missing expected starting delimiter in regular expression "%s", supported delimiters are: %s

What it means

When compiling a 'regex'/'not regex' where clause, a string pattern must be a preg-style pattern with a leading delimiter (e.g. /pattern/i). The library checks the first character against its supported delimiter list and throws LogicException if it isn't one, because it cannot parse flags or the pattern body otherwise.

Solutions

  1. Wrap the pattern in delimiters: ->where('name', 'regex', '/^abc$/i').
  2. Choose a supported delimiter character as the first char (e.g. / or ~) matching self::REGEX_DELIMITERS.
  3. If the value may already contain delimiters, detect and only wrap when substr($pattern, 0, 1) is not a delimiter.
  4. Precompile to a MongoDB\BSON\Regex object to bypass string parsing: ->where('name', 'regex', new Regex('^abc$', 'i')).

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) && !in_array(substr($pattern, 0, 1), ['/', '#', '~'], true)) {
    $pattern = '/' . trim($pattern, '/') . '/';
}

Prevention

When it happens

Trigger: ->where('name', 'regex', '^abc$') (no / delimiters); passing a Mongo shell-style BSON regex string without delimiters; building the regex from concatenation that loses the leading delimiter; REGEX_DELIMITERS mismatch such as using # or unsupported chars.

Common situations: Copy-pasting patterns from Mongo shell or regex101 (which use bare patterns or different delimiters); user-supplied search strings wrapped in code not delimiters.

Related errors


AI-assisted analysis of mongodb/laravel-mongodb@0634653039 (2026-09-15). Data as JSON: /api/errors/4e3eb26ca3b8b21f. Report an issue: GitHub.

Appendix: source

Thrown at src/Query/Builder.php:1510

                // All backslashes are converted to \\, which are needed in matching regexes.
                preg_quote($value),
            );
            $flags = $where['caseSensitive'] ?? false ? '' : 'i';
            $value = new Regex('^' . $regex . '$', $flags);

            // 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';
        }

View on GitHub (pinned to 0634653039)