xai-org/x-algorithm · error · SemanticCheckFailure

class to be imported has to be either a TBase or ThriftStruc

Error message

class to be imported has to be either a TBase or ThriftStruct: %s.

What it means

The `import` statement in a Botmaker expression must reference a Thrift-generated type. TypeCompiler resolved the class, but it was neither a ThriftType (TBase) nor a ThriftStructType, so the compiler refuses to bind it into the type context.

Source

Thrown at botmaker/src/java/com/twitter/botmaker/compiler/Compiler.java:710

    try {
      ASTNode node = createASTNodeTree(context, child);
      return ToThrift.mkNewThriftObject(exprText, className, node);
    } catch (Exception e) {
      throw new SemanticCheckFailure(e);
    }
  }

  private ASTNode toImport(CompilerContext context, Tree root) throws SemanticCheckFailure {

    String clsName = getFullName(root);
    String alias = root.getChild(root.getChildCount() - 1).getText();
    Type type = TypeCompiler.getTypeFromClassName(clsName);
    if (type instanceof ThriftType) {
      context.defineType((ThriftType) type, alias);
    } else if (type instanceof ThriftStructType) {
      context.defineType((ThriftStructType) type, alias);
    } else {
      throw new SemanticCheckFailure(
          String.format(
              "class to be imported has to be either a TBase or ThriftStruct: %s.", clsName)
      );
    }

    return Constant.UNIT;
  }

  private ASTNode toImportAs(CompilerContext context, Tree root) throws SemanticCheckFailure {

    String cls = getFullName(root, root.getChildCount() - 1);
    String alias = root.getChild(root.getChildCount() - 1).getText();

    Class<?> clazz;
    try {
      clazz = ClassCache.forName(cls);
    } catch (Exception e) {
      throw new SemanticCheckFailure(

View on GitHub (pinned to 24c60942c5)

Solutions

  1. Replace the imported class with a Thrift-generated TBase or ThriftStruct class
  2. Regenerate the Thrift/Scrooge classes with the codegen version matching the Botmaker runtime
  3. Verify the class is on the compile classpath and is the generated struct, not a hand-written wrapper

Example fix

// before
import("com.example.MyPlainPojo", pojo)
// after
import("com.example.thriftscala.MyThriftStruct", myStruct)
Defensive patterns

Strategy: validation

Validate before calling

Class<?> c = ClassCache.forName(clsName);
if (!TBase.class.isAssignableFrom(c) && !ThriftStruct.class.isAssignableFrom(c))
  throw new IllegalArgumentException(clsName + " is not a Thrift type");

Type guard

static boolean isImportableThriftType(Class<?> c) {
  return TBase.class.isAssignableFrom(c) || ThriftStruct.class.isAssignableFrom(c);
}

Try / catch

catch (SemanticCheckFailure e) { /* surface import name and fix config */ }

Prevention

When it happens

Trigger: Using `import("com.example.SomeClass", alias)` where SomeClass is a plain Java class, an interface, or a Thrift class generated by an incompatible codegen version.

Common situations: Trying to import java.lang or guava classes, importing a Thrift struct compiled against a mismatched thrift/scrooge runtime, or importing a class not on the compiler's classpath (which may resolve to a stub).

Related errors


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