Skip to content

[BUG][code-analyzer] sfge: a cast or array load inside a new SObject(Field = ...) constructor aborts the entry point #2089

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

A cast or an array load used as a value inside a new SObject(Field = ...) constructor aborts path evaluation. Both are the same underlying contract problem in SFVertexFactory.

NewKeyValueObjectExpressionVertex loads its item vertices with a supplemental parameter (NewKeyValueObjectExpressionVertex.java:57-65 passes ExpressionType.KEY_VALUE). SFVertexFactory.load then requires a two-argument constructor:

// SFVertexFactory.java:231-238
if (supplementalParam != null) {
    Constructor constructor = optClazz.get().getDeclaredConstructor(Map.class, Object.class);
    result = (T) constructor.newInstance(vertexProperties, supplementalParam);
}

getDeclaredConstructor does not search superclasses.

A. Cast. CastExpressionVertex declares only (Map), so the lookup throws NoSuchMethodException, rewrapped as UnexpectedException at SFVertexFactory.java:245.

Account a = new Account(Name = (String) rawName);

B. Array load. ArrayLoadExpressionVertex has the two-argument constructor but rejects the supplemental parameter outright:

// ArrayLoadExpressionVertex.java:34-38
ArrayLoadExpressionVertex(Map<Object, Object> properties, Object supplementalParam) {
    super(properties, TRAVERSAL_CONSUMER);
    if (supplementalParam != null) {
        throw new UnexpectedException(supplementalParam);
    }
}

Since ChainedVertex(Map, Object) discards the supplemental parameter anyway — its own constructor is annotated // TODO: supplementalParam is ignored — there is nothing for that branch to protect. Note the user-visible message for B is null; the informative KEY_VALUE payload only reaches the sfge log.

Output / Logs

# A — cast as a key-value item
UnexpectedException: com.salesforce.graph.vertex.CastExpressionVertex.<init>(java.util.Map, java.lang.Object):
com.salesforce.graph.vertex.SFVertexFactory.load(SFVertexFactory.java:245);
com.salesforce.graph.vertex.SFVertexFactory$1.apply(SFVertexFactory.java:186);
com.salesforce.graph.cache.AbstractVertexCacheImpl.get(AbstractVertexCacheImpl.java:101);
com.salesforce.graph.vertex.LazyVertexList.initialize(LazyVertexList.java:47)

# B — array load as a key-value item (violation message is just "null")
UnexpectedException: null: com.salesforce.graph.vertex.SFVertexFactory.load(SFVertexFactory.java:245); ...
Caused by: com.salesforce.exception.UnexpectedException: KEY_VALUE
	at com.salesforce.graph.vertex.ArrayLoadExpressionVertex.<init>(ArrayLoadExpressionVertex.java:37)

Steps To Reproduce

  1. Create an empty SFDX project (sfdx-project.json with a single force-app package directory).

  2. Add both classes under force-app/main/default/classes/, each with a standard .cls-meta.xml (apiVersion 62.0):

    public with sharing class CastInKeyValue {
        @AuraEnabled
        public static void run(Object rawName) {
            Account a = new Account(Name = (String) rawName);
            insert a;
        }
    }
    public with sharing class ArrayLoadInKeyValue {
        private static final List<String> PARTS = String.valueOf(ArrayLoadInKeyValue.class).split('[.]', 2);
        @AuraEnabled
        public static void run() {
            insert new Account(Name = PARTS[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. Both classes report an InternalExecutionError and yield no findings for their entry point.

Expected Behavior

Both should analyse normally. new Account(Name = (String) x) and new Account(Name = list[0]) are ordinary Apex.

The narrow fix for A is one constructor, mirroring the eleven vertex classes that already have it (BinaryExpressionVertex, BooleanExpressionVertex, MethodCallExpressionVertex, …). Since ChainedVertex(Map, Object) ignores the parameter, it is a pure delegation. The fix for B is to drop the throw on that branch.

A more durable fix would be for SFVertexFactory.load to fall back to the one-argument constructor when the two-argument form is absent, rather than requiring every vertex type to opt in.

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)

Blast radius. CastExpression and ArrayLoadExpression are not the only vertices that can appear as a key-value item. Auditing graph/vertex/ on code-analyzer-core@dev, 15 other concrete ChainedVertex descendants also lack the two-argument constructor (and have no Builder.create(Map, Object) to stand in), so they should fail the same way: AssignmentExpressionVertex, BindExpressionsVertex, ClassRefExpressionVertex, EmptyReferenceExpressionVertex, InstanceOfExpressionVertex, MapEntryNodeVertex, NewKeyValueObjectExpressionVertex, NewObjectExpressionVertex, ParameterVertex, PostfixExpressionVertex, ReferenceExpressionVertex, SoqlExpressionVertex, SuperMethodCallExpressionVertex, ThisMethodCallExpressionVertex, TriggerVariableExpressionVertex.

Related: #886 reported a CastExpressionVertex UnexpectedException in 2022 from a different path (MethodPathBuilderVisitor on switch on (String) x); #973 reported the array-load half. Both were closed in the 2026-06-30 pre-v5 sweep; this is a different call path on 5.15.0.

Workaround

Hoist the expression into a local first:

String name = (String) rawName;
Account a = new Account(Name = name);

Works, but it means the constructor-initialiser syntax cannot be used with a cast or an index anywhere in the codebase.

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