xai-org/x-algorithm · error · SemanticCheckFailure

Contains expects input of String, Map, Set or Collection typ

Error message

Contains expects input of String, Map, Set or Collection types but %s received

What it means

Contains() only accepts a first argument whose static return type is String, Set, Collection, Map, or a generic Object. Any other typeBase (e.g. Long, Boolean, StructTuple) fails semantic checking with this message. Note that a non-Set Collection is automatically coerced via ToSet before containment is checked.

Source

Thrown at botmaker/src/java/com/twitter/botmaker/function/collection/Contains.java:55

  private static final Signature SIGNATURE = new Signature(
      ImmutableList.of(
          Type.OBJECT,
          Type.OBJECT),
      Type.BOOLEAN
  );

  public Contains(String exprText, ImmutableList<ASTNode> children) throws SemanticCheckFailure {
    super(exprText, validateASTNodesAndModifyType(children));
  }

  private static ImmutableList<ASTNode> validateASTNodesAndModifyType(
      ImmutableList<ASTNode> children) throws SemanticCheckFailure {
    Class<?> typeBase = children.get(0).getReturnType().typeBase;
    if (!Object.class.equals(typeBase)
        && !String.class.equals(typeBase)
        && !Collection.class.isAssignableFrom(typeBase)
        && !Map.class.isAssignableFrom(typeBase)) {
      throw new SemanticCheckFailure(String.format(
          "Contains expects input of String, Map, Set or Collection types but %s received",
          typeBase.getName()));
    }

    if (Collection.class.isAssignableFrom(typeBase) && !Set.class.isAssignableFrom(typeBase)) {
      ASTNode setifiedCollection = new ToSet("ToSet", ImmutableList.of(children.get(0)));
      return ImmutableList.of(setifiedCollection, children.get(1));
    }
    return children;
  }

  @Override
  public Signature getSignature() {
    return SIGNATURE;
  }

  @Override
  protected CacheLevel getCacheLevel() {

View on GitHub (pinned to 24c60942c5)

Solutions

  1. Change the first argument to a String, Set, Collection or Map expression
  2. If the value might be anything, cast/annotate it as Object so the generic path applies
  3. Wrap the value in a coercion function that produces a collection (e.g. Split for strings)

Example fix

// before
Contains(Count(items), 3)
// after
Contains(items, 3) // or use the collection directly
Defensive patterns

Strategy: type-guard

Validate before calling

// rule-language: coerce unknown input before Contains
Contains(ToString(maybeAny), "needle")

Type guard

// host code: check the child's static type before building Contains
Class<?> base = child.getReturnType().typeBase;
boolean ok = Object.class.equals(base) || String.class.equals(base)
    || Collection.class.isAssignableFrom(base) || Map.class.isAssignableFrom(base);

Prevention

When it happens

Trigger: Passing Contains(someLong, x), Contains(true, x), or Contains(structTuple, x). Also passing a typed value whose inferred typeBase is not in the allowed set.

Common situations: Chaining Contains after a function that returns a scalar (Count, Size), or after refactoring a pipeline so the first argument's type changed from a collection to a scalar.

Related errors


AI-assisted analysis of xai-org/x-algorithm@24c60942c5 (2026-08-28). Data as JSON: /api/errors/82dc12915cb89642. Report an issue: GitHub.