passbolt/passbolt_api · error · InvalidArgumentException

An error has occurred parsing userCustomFilter

Error message

An error has occurred parsing userCustomFilter: {pe->getMessage()}

What it means

When userCustomFilter is a string, it is parsed as an LDAP search filter with LdapTools' Parser. If the string is not a syntactically valid LDAP filter, the ParserException is caught and rethrown as an InvalidArgumentException with the parser's message appended, explaining that the custom filter could not be parsed.

Solutions

  1. Read the appended ParserException message to locate the syntax problem
  2. Fix the userCustomFilter string to be a valid RFC 4515 LDAP filter with balanced parentheses and proper escaping
  3. Validate the filter against the LDAP directory (e.g. ldapsearch with the same filter) before saving it in settings

Example fix

// before
$userCustomFilter = '(objectClass=user';
// after
$userCustomFilter = '(objectClass=user)';
Defensive patterns

Strategy: validation

Validate before calling

try {
    $parsed = \LdapTools\Query\Builder::parse($userCustomFilter); // or Parser::parse
} catch (ParserException $e) {
    // abort before sync
}

Try / catch

try {
    $result = $sync->run();
} catch (InvalidArgumentException $e) {
    if (str_starts_with($e->getMessage(), 'An error has occurred parsing userCustomFilter')) {
        // fix settings, surface to admin
    }
}

Prevention

When it happens

Trigger: Calling _fetchAndInitializeUsersQuery when directorySettings->getUserCustomFilters() returns a malformed string filter, causing Parser::parse() to throw.

Common situations: Hand-written LDAP filter with unbalanced parentheses, missing attribute/value, or wrong escaping (e.g. '(objectClass=user' or '(&(objectClass=user)(department=IT))' typos) entered in directory sync settings.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/3338c48149700343. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltEe/DirectorySync/src/Utility/LdapDirectory.php:560

     *
     * @param \LdapRecord\Query\Builder $query query
     * @return \LdapRecord\Query\Builder
     * @throws \InvalidArgumentException If userCustomFilter callback is used.
     * @throws \InvalidArgumentException If userCustomFilter cannot be parsed.
     */
    private function _customizeUsersQuery(Builder $query): Builder
    {
        $userCustomFilter = $this->directorySettings->getUserCustomFilters();
        if (is_callable($userCustomFilter)) {
            throw new InvalidArgumentException(
                'Using callbacks for userCustomFilter is not supported anymore. Please use LDAP search filter instead.'
            );
        } elseif (is_string($userCustomFilter)) {
            try {
                $filter = Parser::parse($userCustomFilter);
                $query->rawFilter(Parser::assemble($filter));
            } catch (ParserException $pe) {
                throw new InvalidArgumentException(
                    'An error has occurred parsing userCustomFilter: ' . $pe->getMessage()
                );
            }
        }

        return $query;
    }

    /**
     * Customize groups query as per configuration (if available).
     *
     * @param \LdapRecord\Query\Builder $query query
     * @return \LdapRecord\Query\Builder
     * @throws \InvalidArgumentException If groupCustomFilter callback is used.
     * @throws \InvalidArgumentException If groupCustomFilter cannot be parsed.
     */
    private function _customizeGroupsQuery(Builder $query): Builder
    {

View on GitHub (pinned to 31c1bbc10f)