I found a pretty surprising change in platform behavior today.


Minimum Viable Reproduction (MVR)

Script

It's simple to reproduce. Just run this script in Execute Anonymous:

Account a = [SELECT Id FROM Account LIMIT 1];
a.Phone = '1111111111';
system.debug(a.Name);

Expected Behavior

There should be an uncaught exception.

Execute Anonymous Error
Line: 3, Column: 1
System.SObjectException: SObject row was retrieved via SOQL without querying the requested field: Account.Name

Observed Behavior

There is no exception, but the field is seen as null.

[3]|DEBUG|null


Question

Is this an intentional change, or a bug that was introduced as part of Spring 17? Or was it introduced in an earlier release and I just didn't notice?

Clarification

I considered it obvious based on the M in Minimum Viable Reproduction, but I should be explicit that I already discovered the behavior is still as expected if you don't set any fields. It seems that setting a field is what causes the change in behavior.

Account a = [SELECT Id FROM Account LIMIT 1];
system.debug(a.Name); // still throws
  • Possibly related to Get a Map of Populated SObject Fields and subsequent tweaks? – Adrian Larson Feb 13 '17 at 11:14
  • 3
    Ah good find, in that case we have to be extra careful. Having an error is always helpful! – Raul Feb 13 '17 at 11:22
  • Holy grail!!! I might have to check some of my code again, I might have written buggy classes because of this. – Pranay Jaiswal Feb 13 '17 at 16:29
  • @AdrianLarson Do you have access to Premier Support? If not, do you want me to escalate this for you? I suspect you are right and it is related to the changes to getPopulatedFieldsAsMap that track populated fields. – Daniel Ballinger Feb 13 '17 at 18:27
  • Anyone have access to an Org on EU0? It's the only pod still on Winter 17 that could verify the MVR. – Daniel Ballinger Feb 13 '17 at 18:50
up vote 11 down vote accepted

Support has agreed that this behavior is a bug and informed me there is an ETA to fix it (though they did not share that timeline). They did not share if a Known Issue was created, but it looks like this issue was reported as early as last year:

System.SObjectException is not thrown while executing piece of apex code without querying that field

Summary
System.SObjectException is not thrown while executing below apex code without querying that field after put statement.

Repro
- Issue is replicable in any org by executing the below piece of code.

Go to dev console or workbench:

  1. Execute the following anonymous apex: [WORKING AS EXPECTED]

    sobject so = [SELECT Id FROM Contact LIMIT 1]; 
    System.debug(so.get('Name')); 
    

Result: System.SObjectException: SObject row was retrieved via SOQL without querying the requested field: Contact.Name

  1. Execute this similar anonymous apex: [NOT WORKING AS EXPECTED]

    sobject so = [SELECT Id FROM Contact LIMIT 1]; 
    so.put('Description', 'This is a description'); 
    System.debug(so.get('Name')); 
    

Result: No exception is being thrown. The debug statement outputs null.

Workaround
- Please make sure to query that the required fields in your SOQL query.

  • 1
    That was a good find..!! +1 – Rohit Mourya Mar 9 '17 at 14:36

I think you get the exception if you execute the following code

Contact c = [SELECT Id FROM Contact LIMIT 1];
//c.Salutation = 'Dr.'; //Commented
system.debug(c.Name);

I think that salesforce is converting the SObject to a Map of field's when you try to set a field i.e. c.Salutation = 'Dr.';.

But if you directly access the field prior to setting the value of any other field that conversion may not happen and you get an error.

This is what I think may be happening and I do not have any citation for that.

  • 3
    Yes, I already discovered that the behavior change seems specific to when you specify a field and still works as expected if you don't set any fields. I see that I need to clarify somewhat. – Adrian Larson Feb 13 '17 at 14:46
  • 2
    Regardless, without a citation I cannot accept this answer, and it does not even clarify whether or not this behavior is new. – Adrian Larson Feb 13 '17 at 15:20

The same behavior you're seeing happens under Winter 17. I just tested on an internal engineering pod that's still on Winter 17, and saw the same results as you did where Contact.Name debugged as null.

Relevant debug log excerpt:

11:25:15.11 (14099364)|SOQL_EXECUTE_BEGIN|[1]|Aggregations:0|SELECT Id FROM Contact LIMIT 1
11:25:15.11 (49753979)|SOQL_EXECUTE_END|[1]|Rows:1
11:25:15.11 (49841982)|HEAP_ALLOCATE|[1]|Bytes:8
11:25:15.11 (49904840)|HEAP_ALLOCATE|[1]|Bytes:29
11:25:15.11 (50134484)|HEAP_ALLOCATE|[1]|Bytes:8
11:25:15.11 (50238131)|HEAP_ALLOCATE|[1]|Bytes:33
11:25:15.11 (50396815)|HEAP_ALLOCATE|[1]|Bytes:8
11:25:15.11 (51526876)|VARIABLE_ASSIGNMENT|[1]|c|{"s":1,"v":{"Id":"003D000000QelGQIAZ"}}|0x705a61cb
11:25:15.11 (51552578)|STATEMENT_EXECUTE|[2]
11:25:15.11 (51586662)|HEAP_ALLOCATE|[2]|Bytes:3
11:25:15.11 (51849551)|HEAP_ALLOCATE|[2]|Bytes:-4
11:25:15.11 (51916880)|VARIABLE_ASSIGNMENT|[2]|this.Salutation|"Dr."|0x705a61cb
11:25:15.11 (51935877)|STATEMENT_EXECUTE|[3]
11:25:15.11 (52374771)|USER_DEBUG|[3]|DEBUG|null

I happened to review the underlying core platform changes for the getPopulatedFieldsAsMap fix, and don't see how they could have impacted this - the changes were isolated to that method, and resolving an SObject field's value with this syntax doesn't call that method "under the hood".

I suspect (but can't confirm) that the behavior here is due to the special way that Contact.Name is calculated, and not a general change to apex handling of not queried fields.

  • 1
    It has nothing to do with the field or object in question. I discovered this behavior with a custom object and custom field. So it's a mystery when this behavior was introduced, but is it a bug? I don't think setting a field should change the get behavior on an entirely different field which was not queried for. Seems like it should still cause the same exception in both cases? – Adrian Larson Feb 13 '17 at 20:12
  • @AdrianLarson I'd be inclined to agree then. Contact.Name is weird enough that removing it from the equation is good though. If you have premier support I'd encourage you to file a case and share the # for any internal folks to keep an eye on how it progresses. – ca_peterson Feb 13 '17 at 20:21
  • 3
    Case #15723422. – Adrian Larson Feb 13 '17 at 21:51

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.