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