Read the error message carefully: it tells you the type the compiler thinks the variable is, and the argument types it tried to match. Most of the time, the type is broader than you expect (SObject instead of Account, Object instead of your class) and the fix is a cast.
The eight causes
1. Typo or wrong case
User-defined types in Apex are case-insensitive at the type/identifier level, but managed package methods, namespaces, and constants are not always treated identically. Double-check the spelling — easy to miss in a long argument list.
2. Wrong argument types or count
The error message ends with the argument types it tried to match. If it shows (String, Integer) but the method takes (String, Long), that's your bug. Apex auto-coerces some types (Integer → Long) but not others. Cast explicitly:
// Fails: passing Integer where Decimal expected and no implicit widening
calculator.compute(5);
// Works
calculator.compute((Decimal) 5);
calculator.compute(5.0);3. The variable is typed as a parent type — you need a cast
The most common cause in real code. You SOQL into SObject, you iterate over List<SObject>, you accept an Object argument — and the compiler can only see methods on those declared types.
SObject record = [SELECT Id, Name FROM Account LIMIT 1];
// Fails: SObject doesn't declare getName()
String n = record.getName();
// Works: cast to Account first
Account a = (Account) record;
String n = a.Name;4. Static called on instance (or vice versa)
Apex enforces this strictly. System.debug(...) is static — System sys = new System(); sys.debug(...) won't compile. Custom classes get it both ways: an instance method called as MyClass.method(), or a static called as (new MyClass()).method() — both surface as "method does not exist".
5. Method is private or protected, and you're outside its scope
The compiler treats a method it can't see the same way as a method that doesn't exist. If your test class calls a private helper, the error reads identically to a typo. Mark the method @TestVisible (visible only to tests) or promote it to public:
public class OrderService {
@TestVisible
private void calculateDiscount(Order o) { /* ... */ }
}
// OrderServiceTest can now call calculateDiscount6. Generic type erasure — the silent killer
Apex generics are reified only for collection types. Pass a List<Account> into a method that accepts List<SObject>and the method body sees List<SObject> — calls to Account-specific methods fail. Same for Map<Id, Account> vs Map<Id, SObject>. Either cast inside the loop, or make the helper generic by accepting the concrete type.
public static void process(List<SObject> records) {
for (SObject r : records) {
// Account a = r.Account; // doesn't compile
Account a = (Account) r; // ok
System.debug(a.Name);
}
}7. Managed-package method missing the namespace prefix
When your code lives outside a managed package and the method is defined inside one, you must prefix the namespace. fflib_SObjectUnitOfWork.registerNew(...) → someNamespace.fflib_SObjectUnitOfWork.registerNew(...). Subscriber-org code that fails on the same line that works in the package source is almost always this.
8. A field or property shadows the method name
Rare but real: declaring String getName; as a field in the same class as a method String getName() makes the field win — calls to the method now hit the field reference and the compiler complains the method doesn't exist with () applied. Rename the field.
How to read the error message
The error has a strict shape. Knowing the parts helps you triage in seconds:
Method does not exist or incorrect signature:
void buildSummary(String, Integer)
from the type OrderService- The return type (
void) is what the call site expected — usually the assignment target. - The arguments in parens are the types the compiler inferred from what you passed in. If one of them is unexpectedly broad (
Object,SObject), that's a clue. - The type after "from the type" is what the compiler thinks your variable is. If that's not what you expect, cast at the call site.
Catch this at write time — not at deploy time
Salesforce only reports compile errors after you save to the org or run a deploy. Nimbus type-checks Apex locally and the LSP highlights "method does not exist" the moment you type it. Same diagnostic, no round-trip.
# One-shot type-check before commit
nimbus check
# Or run the LSP and get diagnostics live in VS Code / IntelliJ
nimbus lsp