
Apex errors happen to anyone developing code on Salesforce. You can be an admin trying to troubleshoot something, a junior developer tackling a new task for the first time, or a seasoned developer trying to figure out some legacy code. Regardless, you will run into Apex errors from time to time. Here are some common errors and their root causes:
1. Attempt to de-reference a null object
This Apex error is a big one that comes up in all sorts of scenarios. It simply something is returning null! This could be an sObject, a variable, a data class, or a set but something is not being defined or returned properly. For example:
String nullString;
nullString = nullString.capitalize();
Will return this error. The issue here is the nullString is declared as a variable, but never initialized. When calling the capitalize() method, there is nothing for the method to perform its operation on, returning the error.
Because Apex will not allow you to use an undefined variable, one of your existing variables is either not being initialized properly or it is the return value of a method that is not returning what you expect. For example:
DOM.Document doc = new DOM.Document();
dom.XmlNode rootNode = doc.createRootElement('Envelope', 'namespace', 'soapenv');
String message = rootNode.getChildElement('Body', 'diffnamespace').getText();
Will also return the error. The part here that is null is the getChildEleent() return value, so when you call getText() on it, it is trying to preform it on a null value. More complex or custom methods may not have robust handling of null return and parameter values, so they need to be scrutinized when you encounter this error.
2. List has no rows for assignment to sObject
You will see this Apex error when a SOQL query assigning the value of a single sObject is not returning a value. Double-check what your SOQL criteria is and make sure it is logical. If it is and you are still seeing this error, try running the query in real-time using the developer console. If this is not returning records, you’ll have to edit your criteria or figure out why a record you expect to be there is not.
To prevent this error from happening in the first place, it is possible to always assign your SOQL query results to a list of the sObject you are expecting to get back. A list can accept query results that are empty. Then, simply check if your list is not empty and you can then use the value in the list for whatever you needed it for:
List<Account> emptyList = [SELECT Id FROM Account WHERE Type = 'Non-Account'];
if(emptyList.size() > 0){
//condition if true
}
3. List index out of bounds: [n]
You will see this Apex error if you are trying to access a list index that is outside the indices of that list. For example:
List<Account> accountList = [Select Id FROM Account LIMIT 1];
Account outOfBounds = accountList[1];
In this example, the list accountList only has one value, the value at index 0. By then trying to access the value at index 1, we get the error that this is out of bounds.
In cases where this error is not as obvious, double-check your variables that are accessing indices and make sure they are not accessing something that does not exist. Similarly, if you are accessing indices you do expect to exist, check out the lists themselves. They may be empty or they may not have the correct number of elements in them, for various reasons. You can also use the list method list.size() to determine how many indices are in the list itself.
Also, the [n] in the error is the index you are trying to reference that does not exist. This is a handy indicator of the error’s location. If you are using a variable to access an index, you will know what value it throws the error. If it is being called discretely, you will know what line to look at in your code.
4. Invalid index at id [n]: null
I was working on debugging some code once and I ran into the above Apex error. It was stumping me for a while because I was checking to make sure records existed, could be queried, etc., but the culprit was something I had simply overlooked. I had no ID to work with! Take a look at the following code:
Opportunity oppNotInserted = new Opportunity();
delete oppNotInserted;
The code I was looking at was essentially adding an object to a list that was being deleted, but the object had not been inserted yet. There’s nothing to delete using that command in the database if it was never inserted in the first place. If I similarly tried to update it without inserting:
Opportunity oppNotInserted = new Opportunity();
update oppNotInserted;
The error ‘Id not specified in update call‘ is returned. Either of these errors mean you are trying to make a DML statement on something that is not yet a record.
5. SObject row was retrieved via SOQL without querying the requested field: [field]
This is a simple Apex error to resolve. The issue here is I made a query to get an sObject and it referenced a field that was not explicitly called in the query. For example:
Account queriedAccount = [SELECT Id FROM Account LIMIT 1];
String accountName = queriedAccount.Name;
Will result in this error. The issue here is the queriedAccount sObject was retrieved without including the field ‘Name.’ If you see this error, simply add the missing field to the query in question. Also, only include fields you need in the query! You are retrieving this record for a reason, so make sure you only get the information you need.
6. Method does not exist or incorrect signature: [method]
This Apex error occurs when you reference a method incorrectly. It can be a little frustrating at first, but once you understand how to troubleshoot it, it is simple to solve.
Look at the reference in the error and ask yourself the following:
- Did I include the correct number of parameters?
- Are the parameters the right type?
- Does the object/type method I am calling exist?
- Does the method I am calling exist in the class in a static context?
The error has to lie in one of these areas. If you are calling an error you see in documentation, like Salesforce standard methods, double-check that you are calling the method on the right class/type/object. If this is an error you are seeing with a custom method, confirm you are calling the right class and your types are correct.
Also, if you update existing code, you will need to update all the areas (including test classes) that reference the code you changed. This may seem obvious, but it helps to think simply when troubleshooting.
7. Recursive Trigger Error
You will encounter this Apex error when you have a recursive trigger situation. Recursive triggers occur when a trigger on Object A makes an update to Object B. Object B then has its own trigger that makes an update to Object A. This will trigger Object A’s trigger again and repeat into an infinite loop. Salesforce can detect these situations and it will throw an error if this happens.
There is a decent amount of content out there on how to get around this issue. I have seen a lot of people recommend using a flag called runOnce, or something similar, and check its context when the triggers fire. The idea is you only run the triggers while the value of runOnce = false, then set it to true. This works, but it is not really a solution!
I recommend taking a step back at what your triggers are actually doing instead. Your code is analogous to a business process. If triggers are firing repeatedly, something is wrong with the process. Look at what is really happening and add some conditions to your triggers so they don’t fire wholesale. There is no business process out there that results in an infinite loop, or else work wouldn’t get done!
8. MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): [sObjects]
This Apex error can show up in test and non-test contexts. Salesforce does not allow you to insert objects before or after certain objects due to how those changes would propagate in the database. The list of objects can be found here.
This error occurs when two separate DML transaction are being made on objects that do not allow insertions in the same transaction. In a non-test scenario, you will have to wrap one of the transactions in a @future context. This means that the transaction will be processed asynchronously (which just means it will be done separately).
If the context is a test method, you have two options:
- Initially create some of the objects in the @testSetup method Salesforce allows for test classes. This method runs first and creates test data that all test methods in the class can access.
- Use the System.runAs([User]) method to run a DML transaction as a different user. Salesforce will allow test classes to treat this as a different transaction.
I have used both solutions, but I found using the @testSetup method is cleaner and much easier to understand.
9. Too many SOQL queries: 101
This is an Apex error that is very common, especially for developers who are getting used to Saleforce governor limits for the first time. Salesforce will not allow you to make more than 100 SOQL queries in a single transaction. A transaction is defined as the start of an operation seen through to end of itself and any other operations it could have triggered. For example:
Save a record --> custom validation code fires --> sObject trigger fires --> configuration rules fire --> sObject trigger fires --> record is saved
In the above sequence, there cannot be more than 100 queries from the operation of the record being saved until the record is finally saved in the database. If this is the case, some of the configuration may need to be consolidated into code. This helps because a developer can bulkify operations.
You may also be seeing this error because you are introducing code into the system that is not bulkified. For example:
List<Account> accountList = [Select Id FROM Account];
for(Account a : accountList){
List<Opportunity> oppList = [SELECT Id FROM Opportunity WHERE AccountId = :a.Id];
//use opp data here
}
A common case of not bulkifying is when you are using an object to get some information about another, related object. In the above case, the limit will trigger because I have more than 100 accounts in that org. I need the opportunities related to the accounts, but this is not the correct way to do it. To bulkify, I will use a map instead.
A Salesforce map is a collection of key-value pairs. You can declare any sObject or datatype as either a key or value. Also, you can have values be maps or lists, which can make for some interesting data structures. To bulkify the above example:
List<Account> accountList = [Select Id FROM Account];
List<Opportunity> oppList = [SELECT Id, AccountId FROM Opportunity];
Map<Id, Opportunity> accountToOppMap = new Map<Id, Opportunity>();
for(Opportunity o : oppList){
accountToOppMap.put(o.AccountId, o);
}
for(Account a : accountList){
if(accountToOppMap.containsKey(a.Id)){
Opportunity oppToUse = accountToOppMap.get(a.Id);
//use opp value here
}
}
Now, this is a bit more complicated, but we went from 100+ SOQL queries to 2! I made a map of Id –> Opportunity and I made the keys the related AccountIds. In the loop itself, I used the map method containsKey() to check if the account I am on exists in the map. If it does, I grab the Opportunity using the get method and the ID of the account. I can then use it as I please.
If you’re still running into SOQL query errors, or any of the errors above, leave a comment or contact me for advice.