Print this page

What does the Invalid Query Locator error mean?

Knowledge Article Number 000004410
Description

What does the Invalid Query Locator error mean?

Resolution

Overview of query locators

A query locator represents a server-side cursor. Typically a query locator is returned when not all the records requested in a query fit into the returned data set. This way a queryMore() can be issued with the provided query locator to get additional rows.

To exemplify this consider a query API call where the batch size is set to 2,000 and the organization has 2000+ accounts. In this scenario, SELECT id FROM account will return 2,000 records and a query locator to retrieve the remaining records.


Maximum number of query locators per user and INVALID_QUERY_LOCATOR returned in an API query call

Each Salesforce user can have up to 10 query cursors open at a time. This is a hard coded limit that cannot be increased. If 10 query locator cursors are opened when a client application attempts to open a new one, then the oldest cursor is released. If a call is attempted to an expired or released cursor, salesforce.com will return the INVALID_QUERY_LOCATOR error. To avoid this error, ensure that the client code is not holding open more than 10 query locator cursors.  If multiple processes are running using the same salesforce.com user, make sure that their combined query locator cursor count remains at 10 or below.


Invalid query locator in Apex triggers or Apex controllers

Additionally in Apex triggers or Apex controllers this error message may be thrown in different scenarios but they are typically caused by using queries with inner queries. In this case the system may transparently to the user generate a query locator so that if Apex code tries to retrieve the child records selected by the inner query, a queryMore() will be used to retrieve them.

As an example, consider the following query:

List<Account> accountsAndCases= [SELECT id, name, (SELECT id, subject, description FROM Cases) FROM Account];

 

where cases can be retrieved as follows:

 

List<Case> casesToUpdate = new List<Case>();

for(Account a : accountsAndCases) // queryMore() will be used to retrieve child cases

casesToUpdate.addAll(a.Cases);


However if the child records are not retrieved immediately, thus "consuming" the generated query locators, these query locators may either

1) Expire after 15 minutes (documented in the Web Services API Developer's Guide).

2) Or get released as new query locators may need to be created depending on custom code path. This can occur if additional queries are inserted between the point the query locators are generated and the point they are consumed.


To avoid this issue you should retrieve the child records within the same scope where the query locators where generated. As a rule of thumb avoid approaches like the following:

1  List<Account> accounts = Utilities.getAccountsAndCases();

2  List<Opportunity> opportunities = Utilities.getOpportunities(accounts);

3  Utilities.updateCases(accounts);


since the system may need to create query locators in the scope of the Utilities.getAccountsAndCases() method, may need to create additional query locators in the scope of Utilities.getOpportunities() and by the time the child account's records are retrieved on line 3, the query locators generated in the first method may no longer be valid. Instead use the following approach:

1  List<Account> accounts = Utilities.getAccountsAndUpdateCases();

2  List<Opportunity> opportunities = Utilities.getOpportunities(accounts);



Invalid query locator in Batch Apex

Similarly to the previous case, if a query with an inner query is used in the start() method of a Batch Apex class, the system may require to generate query locators for the child records that may not get consumed until batch number N, and at that time they may have expired or may have gotten released as a result of other custom code that was executed in batch M, where M < N.

In this case, avoid using inner queries in the start() method of Batch Apex classes and simply query for the child records in the execute() method.





promote demote