Skip to content

[BUG][code-analyzer] sfge: a guard condition on a SOQL result aborts the entry point (TodoException in SchemaBasedValidationAnalyzer) #2095

Description

@willbutler

Have you tried to resolve this issue yourself first?

  • I confirm I have gone through the above steps and still have an issue to report.

Bug Description

Engine: sfge (Salesforce Graph Engine) · Rule: ApexFlsViolation (DevPreview) · Selector: --rule-selector sfge

SchemaBasedValidationAnalyzer.getDerivedApexValue accepts four ApexValue kinds and throws for everything else:

// SchemaBasedValidationAnalyzer.java:85-91
if (!(apexValue instanceof ApexBooleanValue)
        && !(apexValue instanceof ApexCustomValue)
        && !(apexValue instanceof ApexForLoopValue)
        && !(apexValue instanceof ApexSingleValue)) {
    throw new TodoException(
            "What should I do if ApexValue from StandardCondition is not an ApexBooleanValue, "
            + "ApexCustomValue, ApexForLoopValue, or ApexSingleValue: ApexValue=" ...

ApexSoqlValue reaches it easily: ApexSoqlValue.apply returns Optional.empty() for every method it does not model, so a condition calling an unmodelled method on a SOQL-derived value resolves back to the ApexSoqlValue itself.

The triggering shape is "query with WITH USER_MODE, guard, loop, branch on a field, DML" — the single most common shape in a Lightning controller.

Output / Logs

TodoException: What should I do if ApexValue from StandardCondition is not an ApexBooleanValue,
ApexCustomValue, ApexForLoopValue, or ApexSingleValue: ApexValue=ApexValue(ApexSoqlValue)
{status=INITIALIZED, declarationVertex=null, valueVertex=SoqlExpression{...Query=[ SELECT Id, Title,
FileType, Checksum FROM ContentVersion WHERE Id IN :versionIds WITH USER_MODE ]...}, resolvedValues={},
returnedFrom=null, invocableExpression=null, method=null}, parent=Unknown{conditionType=UNKNOWN, ...},
vertex=MethodCallExpressionVertex{fullMethodName=file.FileType.startsWith, ...}:
com.salesforce.rules.fls.apex.operations.SchemaBasedValidationAnalyzer.getDerivedApexValue(SchemaBasedValidationAnalyzer.java:89);
com.salesforce.rules.fls.apex.operations.SchemaBasedValidationAnalyzer.getDerivedApexValue(SchemaBasedValidationAnalyzer.java:117);
com.salesforce.rules.fls.apex.operations.SchemaBasedValidationAnalyzer.checkForValidation(SchemaBasedValidationAnalyzer.java:74); ...

Steps To Reproduce

  1. Create an empty SFDX project (sfdx-project.json with a single force-app package directory).
  2. Add force-app/main/default/classes/SoqlDerivedCondition.cls with the class shown below, plus a standard SoqlDerivedCondition.cls-meta.xml (apiVersion 62.0).
  3. Add code-analyzer.yml:
    engines:
      sfge:
        java_thread_timeout: 900000
        java_thread_count: 4
  4. Run:
    sf code-analyzer run --rule-selector sfge --workspace . --config-file code-analyzer.yml
    
  5. The run reports an InternalExecutionError for the entry point instead of analysing it. That entry point yields no ApexFlsViolation findings at all, and nothing in the summary indicates coverage was lost.
public with sharing class SoqlDerivedCondition {
    @AuraEnabled
    public static void run(Set<Id> versionIds) {
        List<ContentVersion> fileData = [
            SELECT Id, Title, FileType, Checksum
            FROM ContentVersion
            WHERE Id IN :versionIds
            WITH USER_MODE
        ];
        if (fileData == null || fileData.isEmpty()) {
            return;
        }
        List<ContentVersion> out = new List<ContentVersion>();
        for (ContentVersion file : fileData) {
            String endpoint = 'https://example.com/';
            if (file.FileType.startsWith('image')) {
                endpoint += 'images';
            } else {
                endpoint += 'documents';
            }
            file.Title = endpoint;
            out.add(file);
        }
        update out;
    }
}

Expected Behavior

A condition the analyzer cannot interpret is not evidence of anything — it should contribute no schema-based validation and let evaluation continue. The sibling branch twenty lines above already does exactly that:

if (!apexValueOptional.isPresent()) {
    // If standard condition does not resolve to an ApexValue, there isn't much we can do
    return results;
}

Throwing instead costs the whole entry point's coverage over a condition that was never going to be a sanitizer.

Operating System

macOS 26.5.2

Salesforce CLI Version

@salesforce/cli/2.147.7 darwin-arm64 node-v24.5.0

Code Analyzer Plugin (code-analyzer) Version

code-analyzer 5.15.0

Node Version

v24.5.0

Java Version

openjdk version "11.0.32" 2026-07-21

Python Version

N/A

Additional Context (Screenshots, Files, etc)

5 occurrences across 3 distinct @AuraEnabled entry points in our codebase.

Reported verbatim as #1747 in February 2025 and closed as a duplicate of #1497. #1497 has since been closed too, so there is no longer an open home for it.

Workaround

Hoist the field into a local before the condition (String fileType = file.FileType; then branch on fileType). Fragile — it depends on which method the condition calls.

Urgency

Moderate

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions