xai-org/x-algorithm · error · SemanticCheckFailure

Cannot load ThriftStruct Codec for %s: $s

Error message

Cannot load ThriftStruct Codec for %s: $s

What it means

To inspect a scrooge union/struct, getThriftStructCodec reflectively loads the companion class 'StructName$' and its MODULE$ codec. Any reflection failure (class missing, field missing, access error) is wrapped in SemanticCheckFailure.

Source

Thrown at botmaker/src/java/com/twitter/botmaker/compiler/types/ThriftStructType.java:350

    } else if (ThriftEnum.class.isAssignableFrom(runtimeClass)) {
      return new ThriftEnumMetaData((Class<? extends ThriftEnum>) runtimeClass);
    } else {
      throw new SemanticCheckFailure(
          String.format("Unsupported field type %s", runtimeClass.toString()));
    }
  }

  private static ThriftStructCodec getThriftStructCodec(
      Class<? extends ThriftStruct> thriftClass) throws SemanticCheckFailure {

    try {
      Class<? extends ThriftStructCodec> codecClass =
          (Class<? extends ThriftStructCodec>) ClassCache.forName(thriftClass.getName() + "$");
      Field codecField = codecClass.getField("MODULE$");
      ThriftStructCodec codec = (ThriftStructCodec) codecField.get(null);
      return codec;
    } catch (Exception e) {
      throw new SemanticCheckFailure(
          String.format("Cannot load ThriftStruct Codec for %s: $s",
              thriftClass.getName(), e.toString()));
    }
  }

  private static List<ThriftStructFieldInfo> getStructFieldInfos(
      ThriftStructCodec codec) {
    List<ThriftStructFieldInfo> fieldInfos =
        JavaConverters.seqAsJavaList(codec.metaData().fieldInfos());
    return fieldInfos;
  }

  private static List<ThriftStructFieldInfo> getUnionFieldInfos(
      ThriftStructCodec codec) throws SemanticCheckFailure {
    try {
      Method fieldInfosMethod = codec.getClass().getMethod("fieldInfos");
      List<ThriftUnionFieldInfo> fieldInfos =
          JavaConverters.seqAsJavaList(

View on GitHub (pinned to 24c60942c5)

Solutions

  1. Ensure the scrooge-generated class (and its 'StructName$' companion) for the exact schema version is on the classpath
  2. Regenerate thrift code with scrooge, not apache-thrift's generator
  3. Check for shading/relocation rules stripping or renaming companion objects
  4. Align botmaker and thrift artifact versions

Example fix

// before
// classpath contains apache-thrift generated MyStruct (no MODULE$)
Type t = TypeCompiler.getTypeFromClassName("com.acme.MyStruct");
// after
// depend on scrooge-generated MyStruct with MyStruct$ companion on classpath
Type t = TypeCompiler.getTypeFromClassName("com.acme.MyStruct");
Defensive patterns

Strategy: validation

Validate before calling

try {
  Class.forName(thriftClassName + "$");
} catch (ClassNotFoundException e) { /* companion missing: bail early */ }

Try / catch

catch SemanticCheckFailure starting with 'Cannot load ThriftStruct Codec' and report a classpath/version problem rather than a rule error

Prevention

When it happens

Trigger: ClassCache.forName(thriftClass.getName() + "$") failing — the companion object class is absent from the classpath, shaded/renamed, or the thrift class is not scrooge-generated (e.g. apache-thrift generated, which has no MODULE$).

Common situations: Version mismatch between the thrift-generated jar and the botmaker compiler; proguard/shading stripping companion objects; using apache-thrift generated classes where scrooge classes are expected; classloader isolation in app servers.

Related errors


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