---
title: Debug a missing index
description: This example explains how you, as directory administrator, investigate an indexing problem.
component: pingds
version: 8.1
page_id: pingds:config-guide:idx-debug
canonical_url: https://docs.pingidentity.com/pingds/8.1/config-guide/idx-debug.html
revdate: 2025-10-22T14:42:39Z
keywords: ["LDAP", "Troubleshooting"]
section_ids:
  how_it_looks_to_the_application: How it looks to the application
  how_it_looks_to_the_administrator: How it looks to the administrator
  how_the_fix_looks_to_the_administrator: How the fix looks to the administrator
  how_the_fix_looks_to_the_application: How the fix looks to the application
  more_to_do: More to do?
---

# Debug a missing index

This example explains how you, as directory administrator, investigate an indexing problem.

## How it looks to the application

In this example, an LDAP client application helps people look up names using mobile telephone numbers. The mobile numbers are stored on the `mobile` attribute in the directory.

The LDAP client application has a search for a mobile number failing with error 50:

```console
$ ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --trustStorePath /path/to/opendj/config/keystore \
 --trustStoreType PKCS12 \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=user.1,ou=people,dc=example,dc=com \
 --bindPassword password \
 --baseDN dc=example,dc=com \
 "(mobile=14120300042)" cn
```

> **Collapse: Show output**
>
> ```
> # The LDAP search request failed: 50 (Insufficient Access Rights)
> # Additional Information:  You do not have sufficient privileges to perform an unindexed search
> ```

The application owner tells you there's a problem searching on mobile numbers.

## How it looks to the administrator

As administrator, you can observe the failures in the DS access logs. The following example includes only the relevant fields of an access log message with the failure:

```json
{
  "request": {
    "operation": "SEARCH",
    "dn": "dc=example,dc=com",
    "scope": "sub",
    "filter": "(mobile=14120300042)"
  },
  "response": {
    "status": "FAILED",
    "statusCode": "50",
    "detail": "You do not have sufficient privileges to perform an unindexed search",
    "additionalItems": {
      "unindexed": true
    },
    "nentries": 0
  },
  "userId": "uid=user.1,ou=People,dc=example,dc=com"
}
```

For this simple filter, `(mobile=14120300042)`, if the search is unindexed, you can conclude that the attribute must not be indexed. As expected, the `mobile` attribute does not appear in the list of indexes for the backend:

```console
$ dsconfig \
 list-backend-indexes \
 --backend-name dsEvaluation \
 --hostname localhost \
 --port 4444 \
 --bindDN uid=admin \
 --bindPassword password \
 --trustStorePath /path/to/opendj/config/keystore \
 --trustStoreType PKCS12 \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --no-prompt
```

> **Collapse: Show output**
>
> ```
> Backend Index              : index-type             ...
> ---------------------------:------------------------...
> aci                        : presence               ...
> cn                         : equality, substring    ...
> ds-certificate-fingerprint : equality               ...
> ds-certificate-subject-dn  : equality               ...
> ds-sync-conflict           : equality               ...
> ds-sync-hist               : ordering               ...
> entryUUID                  : equality               ...
> givenName                  : equality, substring    ...
> json                       : equality               ...
> jsonToken                  : equality               ...
> mail                       : equality, substring    ...
> manager                    : equality, extensible   ...
> member                     : equality               ...
> oauth2Token                : equality               ...
> objectClass                : big-equality, equality ...
> sn                         : equality, substring    ...
> telephoneNumber            : equality, substring    ...
> uid                        : equality               ...
> uniqueMember               : equality               ...
> ```

You can determine why a filter is unindexed by:

* Referring to the `response` > `additionalItems` > `debugSearchIndex` object on the access log message for the unindexed search.

* Running the search with the `debugsearchindex` attribute.

If the data and indexes changed significantly since the search ran, the `debugSearchIndex` object and `debugsearchindex` output can be different.

You notice that `telephoneNumber` has equality and substring indexes, and decide to add the same for the `mobile` attribute. Adding a new index means adding the configuration for the index, and then building the index. An index is specific to a given server, so you do this for each DS replica. For example:

```console
$ dsconfig \
 create-backend-index \
 --backend-name dsEvaluation \
 --index-name mobile \
 --type generic \
 --set index-type:equality \
 --set index-type:substring \
 --hostname localhost \
 --port 4444 \
 --bindDN uid=admin \
 --bindPassword password \
 --trustStorePath /path/to/opendj/config/keystore \
 --trustStoreType PKCS12 \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --no-prompt
$ rebuild-index \
 --index mobile \
 --hostname localhost \
 --port 4444 \
 --bindDN uid=admin \
 --bindPassword password \
 --baseDN dc=example,dc=com \
 --trustStorePath /path/to/opendj/config/keystore \
 --trustStoreType PKCS12 \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin
```

## How the fix looks to the administrator

Once the index is built, you check that the search is now indexed by looking at the `debugsearchindex` output:

```console
$ ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --trustStorePath /path/to/opendj/config/keystore \
 --trustStoreType PKCS12 \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=admin \
 --bindPassword password \
 --baseDN dc=example,dc=com \
 "(mobile=14120300042)" \
 debugsearchindex | sed -n -e "s/^debugsearchindex: //p"
```

> **Collapse: Show output**
>
> ```
> {
>     "baseDn": "dc=example,dc=com",
>     "scope": "sub",
>     "filter": "(mobile=14120300042)",
>     "maxCandidateSize": 100000,
>     "lookThroughLimit": 0,
>     "strategies": [
>         {
>             "name": "BaseObjectSearchStrategy",
>             "diagnostic": "not applicable",
>             "lookedThrough": 0
>         },
>         {
>             "name": "VlvSearchStrategy",
>             "diagnostic": "not applicable",
>             "lookedThrough": 0
>         },
>         {
>             "name": "AttributeIndexSearchStrategy",
>             "filter": {
>                 "query": "FIRST_OF",
>                 "rank": "EXACT_MATCH",
>                 "filter": "(mobile=14120300042)",
>                 "subQueries": [
>                     {
>                         "query": "EXACT_MATCH",
>                         "rank": "EXACT_MATCH",
>                         "filter": "(mobile=14120300042)",
>                         "index": "mobile.telephoneNumberMatch",
>                         "key": "14120300042",
>                         "diagnostic": "indexed",
>                         "candidates": 1
>                     },
>                     {
>                         "query": "MATCH_ALL",
>                         "rank": "MATCH_ALL",
>                         "filter": "(mobile=14120300042)",
>                         "index": "mobile.presence",
>                         "diagnostic": "skipped"
>                     }
>                 ],
>                 "diagnostic": "indexed",
>                 "candidates": 1
>             },
>             "diagnostic": "indexed",
>             "candidates": 1,
>             "lookedThrough": 1
>         },
>         {
>             "name": "BigIndexSearchStrategy",
>             "diagnostic": "skipped"
>         },
>         {
>             "name": "UnindexedSearchStrategy",
>             "diagnostic": "skipped"
>         }
>     ],
>     "lookedThrough": 1,
>     "final": 1
> }
> ```

You tell the application owner to try searching on mobile numbers now that you have indexed the attribute.

## How the fix looks to the application

The client application now has a working search:

```console
$ ldapsearch \
 --hostname localhost \
 --port 1636 \
 --useSsl \
 --trustStorePath /path/to/opendj/config/keystore \
 --trustStoreType PKCS12 \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --bindDN uid=user.1,ou=people,dc=example,dc=com \
 --bindPassword password \
 --baseDN dc=example,dc=com \
 "(mobile=14120300042)" cn
```

> **Collapse: Show output**
>
> ```
> dn: uid=user.0,ou=People,dc=example,dc=com
> cn: Aaccf Amar
> ```

## More to do?

Must you do anything more? What about `index-entry-limit` settings, for example?

Run the `backendstat show-index-status` command. Optionally, stop the server before running the command:

```console
$ stop-ds --quiet
$ backendstat show-index-status --baseDN dc=example,dc=com
```

> **Collapse: Show output**
>
> ```
> Index Name                                ...  Over  Entry Limit  Mean  ...
> ------------------------------------------...---------------------------...--
> ...
> mobile.telephoneNumberMatch               ...     0         4000     1  ...
> mobile.telephoneNumberSubstringsMatch:6   ...    10         4000     2  ...
> ...
>
> Index: mobile.telephoneNumberSubstringsMatch:6
> Over index-entry-limit keys: [0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
> ...
> ```

Notice that the equality index `mobile.telephoneNumberMatch` has no keys whose entry ID lists are over the limit, and the average number of values is 1. This is what you would expect. Each mobile number belongs to a single user. DS uses this index for exact match search filters like `(mobile=14120300042)`.

Notice also that the substring index `mobile.telephoneNumberSubstringsMatch:6` has 10 keys whose lists are over the (default) limit of 4000 values. These are the keys for substrings that match only a single digit of a mobile number. For example, a search for all users whose mobile telephone number starts with `1` uses the filter `(mobile=1*)`. This search would be unindexed.

Should you raise the `index-entry-limit` for this substring index? Probably not, no.

The filter `(mobile=1*)` matches all mobile numbers for the United States and Canada, for example. Someone running this search is not looking up a user's name by their mobile phone number. They are scanning the directory database, even if it is not intentional. If you raise the `index-entry-limit` setting to prevent any `Over index-entry-limit keys`, the server must update enormous entry ID lists for these keys whenever a `mobile` attribute value changes. The impact on write performance could be a bad tradeoff.

If possible, suggest that the LDAP application refrains from searching until the user has provided enough digits of the mobile number to match, or that it prompts the user for more digits when it encounters an unindexed search.

If you cannot change the application, it might be acceptable that searches for a single mobile telephone digit simply fail. That might be a better tradeoff than impacting write performance due to a very high `index-entry-limit` setting.
