ValError from FeelEngine

Hello,

I hope someone can help with my current issue.

I have a rule in my dmn which looks as follows:

(count(MyInput.OneList[ownOperator(item.AnotherList, "XYZ")]) = 1)

where:

  • MyInput = Input given for evaluating
  • OneList= a list in the input
  • ownOperator = an operator working rather similar to the ‘list contains’-Operator from FEEL (Can’t use the ‘list operator’ here instead for several reasons)
  • AnotherList = a parameter (as List) within the OneList

My Input looks for example like this:

MyInput
	[OneList
		[AnotherList
			[ABC, XYZ]
		], 
		[AnotherList
			[ABC]
		]
	]
	[OneList
		[AnotherList
			[ABC]
		]
	]

I expect my rule to evaluate to true for this input. And it does. But still FeelEngine gives me a warning saying:

ValError(no variable found for name ‘item’)

I only see this warning because in my ownOperator I have implemented a check for ValErrors.
It looks like my ownOperator is called two times, first time with that ValError, second time without.

With debugging I came up to ScalaFeelEngine where it calls feelEngine.evalUnaryTests(expression, context);, but then I don’t know what exactly happens in FeelEngine and why my ownOperator is called twice.

When I change the rule to count[...] = 2, it evaluates to false with the above given input, as expected, so I assume the evaluation of the expression itself works correctly.

I hope I have explained my problem in an understandable way.

Any idea why this is happening?

Thanks for any help,
Belli

Hi @Belli,

it has been a couple of days since you have posted this question. Did you make any progress on your own?

I’m trying to replicate the issue. I use the following input:

{
    MyInput:
        OneList: [
            {
                AnotherList: ["ABC", "XYZ"]
            },
            {
               AnotherList: ["ABC"]
            }
    ]
}

Is this the structure you are using?

Since you said your operator is similar to contains, I evaluated the following expression:

(count(MyInput.OneList[list contains(item.AnotherList, "XYZ")]) = 1)

It evaluates to true — just as expected.
How did you implement your own operator? As a FEEL expression or using the Function Provider SPI?
Can you share the implementation?

Hi @StephanHaarmann,

thanks for your answer.
Unfortunately i was not able to make any progress on this issue on my own so far.

I implemented my operator whitin a CustomFunctionProvider which implements the FeelCustomFunctionProvider.
But I don’t think the implementation of the operator is the problem because its input when we get there the fist time already is: ValError, “XYZ”

The only reason, why I see that ValError at all is because I am checking for ValErrors in the beginning of the operator-implementation and log a warning if there are some.

Also, when we get there the second time without the ValError, my operator also evaluates to true, just as ‘list contains’ is doing.

So still I think something happening in the FeelEngine is the problem causing to give ValError in the first place but then calling my operator a second time without that ValError.

Best regards,
Belli

I’m still trying to replicate the error.
Do you use your expression as a literal expression in a DMN? And do you reference this decision via a business rule task?

So far, I tried replicating it using Feel-Scala. Using the following two classes, everything runs without errors:

package org.camunda.forum;

import org.camunda.feel.context.JavaFunction;
import org.camunda.feel.context.JavaFunctionProvider;
import org.camunda.feel.syntaxtree.ValBoolean;
import org.camunda.feel.syntaxtree.ValError;
import org.camunda.feel.syntaxtree.ValList;
import org.camunda.feel.syntaxtree.ValString;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

public class CustomFunctionProvider extends JavaFunctionProvider {

  private static final Map<String, JavaFunction> functions = new HashMap<>();

  static {
    final JavaFunction function = new JavaFunction(Arrays.asList("l", "v"), args -> {
      try {
        final ValList list = (ValList) args.get(0);
        final ValString value = (ValString) args.get(1);

       args.stream().filter(v -> v instanceof ValError).forEach(err -> System.out.println("Error " + err));

        return new ValBoolean(list.items().contains(value));
      } catch (Exception e) {
        System.out.println(e.getMessage());
        e.printStackTrace();
      }
      return new ValBoolean(false);
    });
    functions.put("customContains", function);
  }

  @Override
  public Optional<JavaFunction> resolveFunction(String functionName) {
    return Optional.ofNullable(functions.get(functionName));
  }

  @Override
  public Collection<String> getFunctionNames() {
    return functions.keySet();
  }
}
package org.camunda.forum;

import org.camunda.feel.FeelEngine;
import scala.util.Either;

import java.util.Arrays;
import java.util.Map;

public class TestApp {

  public static void main(String[] args) {
    FeelEngine engine = new FeelEngine.Builder()
        .functionProvider(new CustomFunctionProvider())
        .build();
    final Map<String, Object> variables = Map
        .of("MyInput", Map
            .of("OneList", Arrays.asList(
                Map.of("AnotherList", Arrays.asList("ABC", "XYZ")),
                Map.of("AnotherList", Arrays.asList("XYZ"))
            )));
    Either<FeelEngine.Failure, Object> result = engine.evalExpression(
        "(count(MyInput.OneList[customContains(item.AnotherList, \"XYZ\")]) = 2)",
        variables);
    if (result.isRight()) {
      final Object value = result.right().get();
      System.out.println("result is " + value);
    } else {
      final FeelEngine.Failure failure = result.left().get();
      throw new RuntimeException(failure.message());
    }
  }
}

@Belli this sounds like a known issue: Custom function invoked with ValError(no variable found for name 'item') when used in list filter condition · Issue #359 · camunda/feel-scala · GitHub

The issue was fixed in the FEEL engine version 1.15.0.

1 Like

Thanks @Philipp_Ossler,

how can I check which FEEL engine version is used right now?
In my pom, I have this only:

      <dependency>
        <groupId>org.camunda.bpm.dmn</groupId>
        <artifactId>camunda-engine-dmn-bom</artifactId>
        <version>7.14.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>

Where do I need to add/change something?

@Belli you check the versions in the GitHub repo, or here in the docs: Changelog | FEEL-Scala

Camunda Platform 7.14.0 uses FEEL in version 1.12.2.

You can change the version but it depends on how you use the Camunda Platform (i.e. embedded, run, etc.). If you embed the engine then it should be possible to change the version by depending on a newer version of the FEEL engine (see the pom).

BTW: you should update your Camunda Platform version :wink:
However, I don’t see a Camunda version that uses FEEL 1.15 already.

1 Like

Thank you so much, @Philipp_Ossler.
Your suggested solution fixed the issue :slight_smile: