Salesforce Dictionary - Free Salesforce GlossarySalesforce Dictionary
All errors
Apex

Initial term of field expression must be a concrete SObject: <type>

You wrote `someThing.Field__c` where `someThing` isn't a specific SObject type — usually an `SObject` or `Object` reference. Apex needs the concrete type at compile time to resolve the field name. Cast to the specific SObject before accessing fields.

Also seen asInitial term of field expression must be a concrete SObject·must be a concrete SObject·concrete SObject apex

Apex's strong typing applies to field access: record.Name works only if Apex knows record is, say, Account (which has a Name). If record is the abstract SObject type or Object, the compiler can't resolve Name and throws this error.

The shape of the bug

SObject record = [SELECT Id, Name FROM Account LIMIT 1][0];
String name = record.Name;   // ❌ Initial term of field expression must be a concrete SObject

The runtime knows it's actually an Account, but the compiler sees SObject and refuses.

Fix 1: cast to the specific type

SObject record = [SELECT Id, Name FROM Account LIMIT 1][0];
Account a = (Account) record;
String name = a.Name;

Or inline:

String name = ((Account) record).Name;

The cast satisfies the compiler. It fails at runtime if the actual record isn't an Account.

Fix 2: use SObject's get() method

For genuinely-untyped code (e.g., a trigger handler that processes any sObject), use the dictionary-style accessor:

SObject record = ...;
String name = (String) record.get('Name');

get() returns Object, which you cast to the right type. Slower and less safe than direct field access, but works without compile-time knowledge of the type.

Fix 3: query into the typed list

If you wrote SObject because the variable was a list:

List<SObject> records = Database.query('SELECT Id, Name FROM Account');   // ❌
for (SObject r : records) { String n = r.Name; }   // boom

If the source is a hard-coded type, use the typed list:

List<Account> records = [SELECT Id, Name FROM Account];
for (Account r : records) { String n = r.Name; }   // ✅

For dynamic SOQL where the type is computed:

List<SObject> records = Database.query(soqlString);
for (SObject r : records) {
    Object name = r.get('Name');
}

A subtle case: relationship traversal

Account a = [SELECT Id, Owner.Email FROM Account LIMIT 1];
String email = a.Owner.Email;   // works if Owner has Email

But if Owner is polymorphic (could be User OR Group), the compiler can't pick one statically:

String email = a.Owner.Email;   // sometimes errors — Owner could be Group, which has no Email

Use TYPEOF in SOQL to disambiguate:

[SELECT Id, TYPEOF Owner WHEN User THEN Email END FROM Account]

Or cast at runtime:

if (a.Owner instanceof User) {
    String email = ((User) a.Owner).Email;
}

When the variable IS a concrete SObject

Three suspects if the message looks wrong:

  1. The variable was declared as SObject somewhere upstream and re-assigned
  2. The method's parameter type is SObject even though you always pass Accounts
  3. A generic helper class returns SObject for portability

Cast at the call site or refactor the helper to return the specific type when known.

Related dictionary terms

Share this fix

Share on LinkedInShare on X