embabel-agent icon indicating copy to clipboard operation
embabel-agent copied to clipboard

ClassNotFoundException when using `void` as return type on `@Action` method

Open tobHai opened this issue 2 months ago • 2 comments

I created a simple agent in Java:

import com.embabel.agent.api.annotation.AchievesGoal;
import com.embabel.agent.api.annotation.Action;
import com.embabel.agent.api.annotation.Agent;
import com.embabel.agent.api.common.Ai;

@Agent(description = "Course")
public class CourseAgent {

   private final CourseRepository courseRepository;

   public CourseAgent(CourseRepository courseRepository) {this.courseRepository = courseRepository;}

   @Action
   public MarketingText generateText(Course course, Ai ai) {
      var prompt = """
                Create a catching marketing text for the following sport course:
                Type: %s, Level: %s, Duration: %s, Location: %s
                """.formatted(
            course.sport(), course.level(), course.durationInHours(), course.location());
      return ai
            .withDefaultLlm()
            .createObject(prompt, MarketingText.class);
   }

   @AchievesGoal(description = "Generate a marketing text for a sport course")
   @Action
   public void updateCourseMarketingText(Course course, MarketingText marketingText) {
      courseRepository.updateMarketingText(course.id(), marketingText.text());
   }

}

It looks to me like the void combined with @AchievesGoal results in a class not found exception:

java.lang.ClassNotFoundException: void
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
	at java.base/java.lang.Class.forName0(Native Method)
	at java.base/java.lang.Class.forName(Class.java:467)
	at java.base/java.lang.Class.forName(Class.java:458)
	at com.embabel.agent.core.JvmType.clazz_delegate$lambda$1(JvmType.kt:57)
	at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
	at com.embabel.agent.core.JvmType.getClazz(JvmType.kt:56)
	at com.embabel.agent.core.JvmType.infoString(JvmType.kt:205)
	at com.embabel.agent.core.AgentScope.infoString$lambda$8(AgentScope.kt:68)
	at kotlin.text.StringsKt__AppendableKt.appendElement(Appendable.kt:85)
	at kotlin.collections.CollectionsKt___CollectionsKt.joinTo(_Collections.kt:3490)
	at kotlin.collections.CollectionsKt___CollectionsKt.joinToString(_Collections.kt:3507)
	at kotlin.collections.CollectionsKt___CollectionsKt.joinToString$default(_Collections.kt:3506)
	at com.embabel.agent.core.AgentScope.infoString(AgentScope.kt:68)
	at com.embabel.agent.core.Agent.infoString(Agent.kt:112)
	at com.embabel.common.core.types.HasInfoString.infoString$default(HasInfoString.kt:28)
	at com.embabel.agent.core.Agent.getPlanningSystem(Agent.kt:101)
	at com.embabel.agent.core.support.BlackboardWorldStateDeterminer.<init>(BlackboardWorldStateDeterminer.kt:38)
	at com.embabel.agent.core.support.SimpleAgentProcess.<init>(SimpleAgentProcess.kt:54)
	at com.embabel.agent.core.support.SimpleAgentProcess.<init>(SimpleAgentProcess.kt:30)
	at com.embabel.agent.core.support.DefaultAgentPlatform.createAgentProcess(DefaultAgentPlatform.kt:173)

It is not a big deal since it can be prevented by using Void as a return type, but that feels a bit clunky.
Maybe the user experience can be improved a bit here?

tobHai avatar Nov 28 '25 12:11 tobHai

Thank you for the report. Will look into it.

johnsonr avatar Nov 28 '25 21:11 johnsonr

The AchievesGoal annotation works by looking at the return type of the goal action method. Otherwise it would be impossible to determine which was the goal type if there were >1 parameters to the goal action.

This would not be an issue for the new utility planning style, but might be problematic for GOAP usage where you expect a particular type from the agent.

Will investigate. The answer may be to flag this as an error if it's in a configuration that doesn't make sense.

johnsonr avatar Nov 28 '25 21:11 johnsonr