Method threw 'java.lang.IllegalArgumentException' exception. Cannot evaluate jdk.proxy3.$Proxy76.toString()

Hi, I’m trying to get the parent from the child workflow in the sample project and it’s throwing me this error :

“Method threw ‘java.lang.IllegalArgumentException’ exception. Cannot evaluate jdk.proxy3.$Proxy76.toString()”

Here’s the code :

/*
 *  Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
 *
 *  Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 *  Modifications copyright (C) 2017 Uber Technologies, Inc.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"). You may not
 *  use this file except in compliance with the License. A copy of the License is
 *  located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 *  or in the "license" file accompanying this file. This file is distributed on
 *  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 *  express or implied. See the License for the specific language governing
 *  permissions and limitations under the License.
 */

package io.temporal.samples.hello;

import io.temporal.client.WorkflowClient;
import io.temporal.client.WorkflowOptions;
import io.temporal.serviceclient.WorkflowServiceStubs;
import io.temporal.worker.Worker;
import io.temporal.worker.WorkerFactory;
import io.temporal.workflow.Async;
import io.temporal.workflow.Promise;
import io.temporal.workflow.Workflow;
import io.temporal.workflow.WorkflowInterface;
import io.temporal.workflow.WorkflowMethod;

/**
 * Sample Temporal Workflow Definition that demonstrates the execution of a Child Workflow. Child
 * workflows allow you to group your Workflow logic into small logical and reusable units that solve
 * a particular problem. They can be typically reused by multiple other Workflows.
 */
public class HelloChild {

  // Define the task queue name
  static final String TASK_QUEUE = "HelloChildTaskQueue";

  // Define the workflow unique id
  static final String WORKFLOW_ID = "HelloChildWorkflow";

  /**
   * Define the parent workflow interface. It must contain one method annotated with @WorkflowMethod
   *
   * @see io.temporal.workflow.WorkflowInterface
   * @see io.temporal.workflow.WorkflowMethod
   */
  @WorkflowInterface
  public interface GreetingWorkflow {

    /**
     * Define the parent workflow method. This method is executed when the workflow is started. The
     * workflow completes when the workflow method finishes execution.
     */
    @WorkflowMethod
    String getGreeting(String name);
  }

  /**
   * Define the child workflow Interface. It must contain one method annotated with @WorkflowMethod
   *
   * @see io.temporal.workflow.WorkflowInterface
   * @see io.temporal.workflow.WorkflowMethod
   */
  @WorkflowInterface
  public interface GreetingChild {

    /**
     * Define the child workflow method. This method is executed when the workflow is started. The
     * workflow completes when the workflow method finishes execution.
     */
    @WorkflowMethod
    String composeGreeting(String greeting, String name);
  }

  // Define the parent workflow implementation. It implements the getGreeting workflow method
  public static class GreetingWorkflowImpl implements GreetingWorkflow {

    @Override
    public String getGreeting(String name) {
      /*
       * Define the child workflow stub. Since workflows are stateful,
       * a new stub must be created for each child workflow.
       */
      GreetingChild child = Workflow.newChildWorkflowStub(GreetingChild.class);

      // This is a non blocking call that returns immediately.
      // Use child.composeGreeting("Hello", name) to call synchronously.

      /*
       * Invoke the child workflows composeGreeting workflow method.
       * This call is non-blocking and returns immediately returning a {@link io.temporal.workflow.Promise}
       *
       * You can use child.composeGreeting("Hello", name) instead to call the child workflow method synchronously.
       */
      Promise<String> greeting = Async.function(child::composeGreeting, "Hello", name);

      // Wait for the child workflow to complete and return its results
      return greeting.get();
    }
  }

  /**
   * Define the parent workflow implementation. It implements the getGreeting workflow method
   *
   * <p>Note that a workflow implementation must always be public for the Temporal library to be
   * able to create its instances.
   */
  public static class GreetingChildImpl implements GreetingChild {

    @Override
    public String composeGreeting(String greeting, String name) {
      Workflow.newExternalWorkflowStub(
          GreetingWorkflow.class, Workflow.getInfo().getParentWorkflowId().get());
      return greeting + " " + name + "!";
    }
  }

  /**
   * With the workflow, and child workflow defined, we can now start execution. The main method is
   * the workflow starter.
   */
  public static void main(String[] args) {

    // Get a Workflow service stub.
    WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();

    /*
     * Get a Workflow service client which can be used to start, Signal, and Query Workflow Executions.
     */
    WorkflowClient client = WorkflowClient.newInstance(service);

    /*
     * Define the workflow factory. It is used to create workflow workers for a specific task queue.
     */
    WorkerFactory factory = WorkerFactory.newInstance(client);

    /*
     * Define the workflow worker. Workflow workers listen to a defined task queue and process
     * workflows and activities.
     */
    Worker worker = factory.newWorker(TASK_QUEUE);

    /*
     * Register the parent and child workflow implementation with the worker.
     * Since workflows are stateful in nature,
     * we need to register the workflow types.
     */
    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class, GreetingChildImpl.class);

    /*
     * Start all the workers registered for a specific task queue.
     * The started workers then start polling for workflows and activities.
     */
    factory.start();

    // Start a workflow execution. Usually this is done from another program.
    // Uses task queue from the GreetingWorkflow @WorkflowMethod annotation.

    // Create our parent workflow client stub. It is used to start the parent workflow execution.
    GreetingWorkflow workflow =
        client.newWorkflowStub(
            GreetingWorkflow.class,
            WorkflowOptions.newBuilder()
                .setWorkflowId(WORKFLOW_ID)
                .setTaskQueue(TASK_QUEUE)
                .build());

    // Execute our parent workflow and wait for it to complete.
    String greeting = workflow.getGreeting("World");

    // Display the parent workflow execution results
    System.out.println(greeting);
    System.exit(0);
  }
}

And this is the test :

/*
 *  Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
 *
 *  Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 *  Modifications copyright (C) 2017 Uber Technologies, Inc.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"). You may not
 *  use this file except in compliance with the License. A copy of the License is
 *  located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 *  or in the "license" file accompanying this file. This file is distributed on
 *  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 *  express or implied. See the License for the specific language governing
 *  permissions and limitations under the License.
 */

package io.temporal.samples.hello;

import static org.mockito.Mockito.*;

import io.temporal.testing.TestWorkflowEnvironment;
import io.temporal.testing.TestWorkflowExtension;
import io.temporal.worker.Worker;
import org.junit.Assert;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

/** Unit test for {@link HelloChild}. Doesn't use an external Temporal service. */
public class HelloChildJUnit5Test {
  private HelloChild.GreetingChild child = mock(HelloChild.GreetingChildImpl.class);

  @RegisterExtension
  public static final TestWorkflowExtension testWorkflowExtension =
      TestWorkflowExtension.newBuilder()
          .setWorkflowTypes(HelloChild.GreetingWorkflowImpl.class)
          .setDoNotStart(true)
          .build();

  @Test
  public void testChild(
      TestWorkflowEnvironment testEnv, Worker worker, HelloChild.GreetingWorkflow workflow) {
    worker.registerWorkflowImplementationFactory(
        HelloChild.GreetingChild.class,
        () -> {
          when(child.composeGreeting(anyString(), anyString())).thenReturn("Bye World!");
          return child;
        });
    testEnv.start();

    // Execute a workflow waiting for it to complete.
    String greeting = workflow.getGreeting("World");
    Assert.assertEquals("Bye World!", greeting);
    verify(child).composeGreeting(eq("Hello"), eq("World"));

    testEnv.shutdown();
  }
}

I wasn’t able to reproduce the problem running the code you posted.

@maxim while debugging it is same for me as well. I was also not able to write test cases

If you evaluate the above expression while debugging the test, it throws the exception.

By debugging, do you mean setting breakpoints? Please follow Java SDK developer's guide - Debugging | Temporal Documentation

Yes, you can set a breakpoint and evaluate this expression. It’s throwing exception.

Please file an issue to get this fixed. Or provide the fix :).