Getting an error inside a test, it does not happen in when run in a real enviroment (test panicked: getState: illegal access from outside of workflow context)

I send as much info as I have, If I can in anyway make it easier for you, like other form of data let me know. Note that this only becomes an issue when I put more than 1 delayed callback. Again this works fine in a real temporal scenario.

It seems to happen here, although im not certain:

func (w *helper) restartActivationWorkflow(ctx workflow.Context) (workflow.ChildWorkflowFuture, error) {
	for _, key := range w.activationWorfklows.Keys() {
		var toCancel = w.activationWorfklows.Get(key)
		if err := workflow.RequestCancelExternalWorkflow(
			ctx,
			toCancel.ID,
			toCancel.RunID,
		).Get(ctx, nil); err != nil {
			return nil, err
		}
	}
	return workflow.ExecuteChildWorkflow(ctx, ActivationLifeCycleWorkflow, &LifeCycleWorkflowsArgs{
		SubscriptionID:  w.subscriptionID,
		FiberOperatorID: w.fiberOperatorID,
	}), nil
}

most of my code:


type helper struct {
	selector             workflow.Selector
	activationWorfklows  *deterministicMap
	terminationWorfklows *deterministicMap
	fiberOperatorID      string
	subscriptionID       string
	logger               log.Logger
}

func (w helper) hasRunningWorkflows() bool {
	return w.activationWorfklows.Len() > 0 || w.terminationWorfklows.Len() > 0
}

func (r *helper) handleTermination(ctx workflow.Context) {
	var cwf workflow.ChildWorkflowFuture
	cwf, err := r.restartTerminationWorkflow(ctx)
	if err != nil {
		return
	}

	var wfe workflow.Execution
	if err = cwf.GetChildWorkflowExecution().Get(ctx, &wfe); err != nil {
		return
	}
	r.terminationWorfklows.Add(wfe.RunID, wfe)
	r.selector.AddFuture(cwf, func(f workflow.Future) {
		defer r.terminationWorfklows.Remove(wfe.RunID)
		var wasTerminated bool
		err := f.Get(ctx, &wasTerminated)
		if errors.Is(err, workflow.ErrCanceled) {
			r.logger.Info("termination workflow was canceled")
			err = nil
		}

		if err != nil {
			return
		}

		if wasTerminated {
			// termination workflow completed successfully, subscription is terminated
			// we should cancel all other workflows
			for _, key := range r.activationWorfklows.Keys() {
				var toCancel = r.activationWorfklows.Get(key)
				if err := workflow.RequestCancelExternalWorkflow(
					ctx,
					toCancel.ID,
					toCancel.RunID,
				).Get(ctx, nil); err != nil {
					return
				}
			}
		}
	})
}

func (r *helper) handleActivation(ctx workflow.Context) {
	var cwf workflow.ChildWorkflowFuture
	cwf, err := r.restartActivationWorkflow(ctx)
	if err != nil {
		return
	}

	var wfe workflow.Execution
	if err = cwf.GetChildWorkflowExecution().Get(ctx, &wfe); err != nil {
		return
	}

	r.logger.Info("activation workflow started", "runID", wfe.RunID, "id", wfe.ID)

	r.activationWorfklows.Add(wfe.RunID, wfe)
	r.selector.AddFuture(cwf, func(f workflow.Future) {
		defer r.activationWorfklows.Remove(wfe.RunID)
		err := f.Get(ctx, nil)
		if errors.Is(err, workflow.ErrCanceled) {
			r.logger.Info("activation workflow was canceled")
		}
	})
}

func (r *helper) cancelTermination(ctx workflow.Context) {
	// simply cancel all termination workflows
	for _, key := range r.terminationWorfklows.Keys() {
		var toCancel = r.terminationWorfklows.Get(key)
		if err := workflow.RequestCancelExternalWorkflow(
			ctx,
			toCancel.ID,
			toCancel.RunID,
		).Get(ctx, nil); err != nil {
			return
		}
	}
}

func SubscriptionWorkflow(ctx workflow.Context) error {
	ctx = workflow.WithActivityOptions(ctx, subOpts)
	wfh := &helper{
		selector:             workflow.NewSelector(ctx),
		activationWorfklows:  &deterministicMap{},
		terminationWorfklows: &deterministicMap{},
		subscriptionID:       workflow.GetInfo(ctx).WorkflowExecution.ID,
		logger:               workflow.GetLogger(ctx),
	}

	sub, err := getSub(ctx, wfh.subscriptionID)
	if err != nil {
		return err
	}
	wfh.fiberOperatorID = sub.FiberOperatorId

	wfh.selector.AddReceive(workflow.GetSignalChannel(ctx, "todos"), func(c workflow.ReceiveChannel, more bool) {
		var wfArgs subscription.SubscriptionWorkflowArgs
		var activities acts.Activities
		c.Receive(ctx, &wfArgs)

		if wfArgs.Todo != "" {
			if err := workflow.ExecuteActivity(ctx, activities.MarkSubscriptionTodoInProgress, &acts.MarkTodoInProgressRequest{
				SubscriptionID: wfh.subscriptionID,
				Todo:           wfArgs.Todo,
			}).Get(ctx, nil); err != nil {
				return
			}
		}

		switch wfArgs.WorkflowType {
		case subscription.CONTINUE:
			var sub *subscriptionpb.Subscription
			sub, err = getSub(ctx, wfh.subscriptionID)
			if err != nil {
				return
			}

			// when continuing it could be that the subscription has a requested termination date, then we need to also start the termination workflow
			if sub.RequestedTerminationDate != nil {
				wfh.handleTermination(ctx)
			}

			wfh.handleActivation(ctx)
		case subscription.CANCEL_TERMINATE:
			wfh.cancelTermination(ctx)
		case subscription.TERMINATION:
			wfh.handleTermination(ctx)
		default:
			wfh.handleActivation(ctx)
		}
	})

	for wfh.selector.HasPending() || wfh.hasRunningWorkflows() {
		wfh.selector.Select(ctx)
	}

	return nil
}

func (w *helper) restartActivationWorkflow(ctx workflow.Context) (workflow.ChildWorkflowFuture, error) {
	for _, key := range w.activationWorfklows.Keys() {
		var toCancel = w.activationWorfklows.Get(key)
		if err := workflow.RequestCancelExternalWorkflow(
			ctx,
			toCancel.ID,
			toCancel.RunID,
		).Get(ctx, nil); err != nil {
			return nil, err
		}
	}
	return workflow.ExecuteChildWorkflow(ctx, ActivationLifeCycleWorkflow, &LifeCycleWorkflowsArgs{
		SubscriptionID:  w.subscriptionID,
		FiberOperatorID: w.fiberOperatorID,
	}), nil
}

func (w *helper) restartTerminationWorkflow(ctx workflow.Context) (workflow.ChildWorkflowFuture, error) {
	for _, key := range w.terminationWorfklows.Keys() {
		var toCancel = w.terminationWorfklows.Get(key)
		if err := workflow.RequestCancelExternalWorkflow(
			ctx,
			toCancel.ID,
			toCancel.RunID,
		).Get(ctx, nil); err != nil {
			return nil, err
		}
	}
	return workflow.ExecuteChildWorkflow(ctx, TerminationLifeCycleWorkflow, &LifeCycleWorkflowsArgs{
		SubscriptionID:  w.subscriptionID,
		FiberOperatorID: w.fiberOperatorID,
	}), nil
}

type LifeCycleWorkflowsArgs struct {
	SubscriptionID  string
	FiberOperatorID string
}

func ActivationLifeCycleWorkflow(ctx workflow.Context, alh *LifeCycleWorkflowsArgs) error {
	...
}

func TerminationLifeCycleWorkflow(ctx workflow.Context, tlh *LifeCycleWorkflowsArgs) (bool, error) {
	...
}

func (w *LifeCycleWorkflowsArgs) handlePreConditions(ctx workflow.Context) error {
	...
}

func (w *LifeCycleWorkflowsArgs) handleActivation(ctx workflow.Context) error {
	...
}

func (w *LifeCycleWorkflowsArgs) handleSuspension(ctx workflow.Context) error {
	..
}

func (w *LifeCycleWorkflowsArgs) handleResumption(ctx workflow.Context) error {
	...
}

func addActionPoint(ctx workflow.Context, apType subscriptionpb.Subscription_ActionPoint_ActionPointType, id, message string) error {
	..
}

func removeActionPoint(ctx workflow.Context, apType subscriptionpb.Subscription_ActionPoint_ActionPointType, id string) error {
	...
}

func (w *LifeCycleWorkflowsArgs) getNextWorkflows(ctx workflow.Context, lifeCycleType subscriptionpb.Subscription_LifeCycleType) (*subscriptionpb.Subscription_Workflows, error) {
	..
}

func (w *LifeCycleWorkflowsArgs) setActivated(ctx workflow.Context) error {
	...
}

func (w *LifeCycleWorkflowsArgs) setSuspended(ctx workflow.Context) error {
	...
}

func (w *LifeCycleWorkflowsArgs) setResumed(ctx workflow.Context) error {
	...
}

func (w *LifeCycleWorkflowsArgs) setTerminated(ctx workflow.Context) error {
	...
}

func (w *LifeCycleWorkflowsArgs) handleWorkflows(ctx workflow.Context, l subscriptionpb.Subscription_LifeCycleType, wfs *subscriptionpb.Subscription_Workflows) error {
	...
}

the actual test:

func (s *WorkflowTestSuite) Test_Activate_Subscription_Workflow_Spam() {
	s.testEnv.OnActivity(subActs.GetSubscriptionByID, mock.Anything, &subscriptionpb.GetByIDParameters{
		Id: "default-test-workflow-id",
	}).Return(&subscriptionpb.Subscription{
		Id:              "default-test-workflow-id",
		AccesspointId:   "ap1",
		FiberOperatorId: "fo1",
	}, nil).Twice()

	s.testEnv.OnActivity(subActs.MarkSubscriptionTodoInProgress, mock.Anything, &activities.MarkTodoInProgressRequest{
		SubscriptionID: "default-test-workflow-id",
		Todo:           "create::default-test-workflow-id",
	}).Return(nil)

	s.testEnv.OnActivity(subActs.GetSubscriptionByID, mock.Anything, &subscriptionpb.GetByIDParameters{
		Id: "default-test-workflow-id",
	}).Return(&subscriptionpb.Subscription{
		Id:                      "default-test-workflow-id",
		AccesspointId:           "ap1",
		State:                   subscriptionpb.Subscription_CREATED,
		OperationalState:        subscriptionpb.Subscription_ACTIVATING,
		RequestedActivationDate: timestamppb.New(s.testEnv.Now().Add(time.Hour)),
	}, nil).Twice()

	s.testEnv.OnActivity(apMocks.GetAccessPointByID, mock.Anything, &accesspointpb.GetByIdParameters{
		Id: "ap1",
	}).Return(&accesspointpb.AccessPoint{
		PopulationId:        "pop1",
		NbpId:               "nbp1",
		HandleSubscriptions: true,
	}, nil).Once()

	s.testEnv.OnActivity(
		subActs.RemoveActionPoint,
		mock.Anything,
		mock.MatchedBy(func(input *subscriptionsdk.RemoveActionPointParams) bool {
			return input.SubscriptionID == "default-test-workflow-id" && input.Key == subscriptionpb.Subscription_ActionPoint_ACTION_POINT_ACCESSPOINT_NOT_READY
		}),
	).Return(nil).Once()

	s.testEnv.OnActivity(
		subActs.GetNextWorkflows,
		mock.Anything,
		&activities.GetNextWorkflowsRequest{
			SubscriptionID: "default-test-workflow-id",
			LifeCycleType:  subscriptionpb.Subscription_ACTIVATION,
		},
	).Return(
		&subscriptionpb.Subscription_Workflows{
			Workflows: []*subscriptionpb.Subscription_Workflows_Workflow{
				{
					Workflow: &subscriptionpb.Subscription_Workflows_WorkflowStep{
						Name:     "todo-workflow",
						Priority: 10,
					},
				},
			},
		},
		nil,
	).Once()

	s.testEnv.OnActivity(
		subActs.BeganWorkflow,
		mock.Anything,
		mock.MatchedBy(func(input *activities.BeganWorkflowArgs) bool {
			return input.Workflow == "todo-workflow" &&
				input.SubscriptionID == "default-test-workflow-id" &&
				input.Lifecycle == subscriptionpb.Subscription_ACTIVATION
		}),
	).Return(nil).Once()

	s.testEnv.OnActivity(
		subActs.CompletedWorkflow,
		mock.Anything,
		mock.MatchedBy(func(input *activities.CompletedWorkflowArgs) bool {
			return input.Workflow == "todo-workflow" &&
				input.SubscriptionID == "default-test-workflow-id" &&
				input.Lifecycle == subscriptionpb.Subscription_ACTIVATION
		}),
	).Return(nil).Once()

	s.testEnv.OnActivity(
		subActs.GetNextWorkflows,
		mock.Anything,
		&activities.GetNextWorkflowsRequest{
			SubscriptionID: "default-test-workflow-id",
			LifeCycleType:  subscriptionpb.Subscription_ACTIVATION,
		},
	).Return(
		&subscriptionpb.Subscription_Workflows{
			Workflows: []*subscriptionpb.Subscription_Workflows_Workflow{},
		},
		nil,
	).Once()

	s.testEnv.OnActivity(
		subActs.SetActivatedDate,
		mock.Anything,
		mock.MatchedBy(func(input *activities.SetActivatedDateArgs) bool {
			return input.SubscriptionID == "default-test-workflow-id"
		}),
	).Return(nil).Once()
	s.testEnv.RegisterDelayedCallback(func() {
		s.testEnv.SignalWorkflow("todos", subscription.SubscriptionWorkflowArgs{
			Todo:         "create::default-test-workflow-id",
			WorkflowType: subscription.CONTINUE,
		})
	}, 0)

	s.testEnv.RegisterDelayedCallback(func() {
		s.testEnv.SignalWorkflow("todos", subscription.SubscriptionWorkflowArgs{
			Todo:         "create::default-test-workflow-id",
			WorkflowType: subscription.ACTIVATION,
		})
	}, time.Minute)
	s.testEnv.ExecuteWorkflow(SubscriptionWorkflow)
	s.NoError(s.testEnv.GetWorkflowError())
	s.True(s.testEnv.IsWorkflowCompleted())
}

full error:

Running tool: /opt/homebrew/bin/go test -timeout 30s -run ^Test_Subscription_Workflow_Tests$ -testify.m ^(Test_Activate_Subscription_Workflow_Spam)$ go.vxfiber.dev/subscription/subscription/workflows -timeout=5m

2025/05/26 11:33:06 DEBUG handleActivityResult: *workflowservice.RespondActivityTaskCompletedRequest. ActivityID 2 ActivityType bss.subscriptionsdk.GetSubscriptionByID
2025/05/26 11:33:06 DEBUG handleActivityResult: *workflowservice.RespondActivityTaskCompletedRequest. ActivityID 3 ActivityType MarkSubscriptionTodoInProgress
2025/05/26 11:33:06 DEBUG handleActivityResult: *workflowservice.RespondActivityTaskCompletedRequest. ActivityID 4 ActivityType bss.subscriptionsdk.GetSubscriptionByID
2025/05/26 11:33:06 INFO  ExecuteChildWorkflow WorkflowType ActivationLifeCycleWorkflow
2025/05/26 11:33:06 INFO  activation workflow started runID default-test-run-id_5_RunID id default-test-run-id_5
2025/05/26 11:33:06 DEBUG handleActivityResult: *workflowservice.RespondActivityTaskCompletedRequest. ActivityID 7 ActivityType bss.subscriptionsdk.GetSubscriptionByID
2025/05/26 11:33:06 DEBUG Auto fire timer TimerID 0 TimerDuration 1m0s TimeSkipped 1m0s
2025/05/26 11:33:06 DEBUG handleActivityResult: *workflowservice.RespondActivityTaskCompletedRequest. ActivityID 9 ActivityType MarkSubscriptionTodoInProgress
2025/05/26 11:33:06 INFO  ExecuteChildWorkflow WorkflowType ActivationLifeCycleWorkflow
--- FAIL: Test_Subscription_Workflow_Tests (0.04s)
    --- FAIL: Test_Subscription_Workflow_Tests/Test_Activate_Subscription_Workflow_Spam (0.04s)
        /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_testsuite.go:1208: FAIL:	bss.subscriptionsdk.GetSubscriptionByID(string,*subscriptionpb.GetByIDParameters)
            		at: [/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/workflow_testsuite.go:409 /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_test.go:638]
        /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_testsuite.go:1208: FAIL:	bss.accesspointsdk.GetAccessPointByID(string,*accesspointpb.GetByIdParameters)
            		at: [/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/workflow_testsuite.go:409 /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_test.go:648]
        /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_testsuite.go:1208: FAIL:	bss.subscriptionsdk.RemoveActionPoint(string,mock.argumentMatcher)
            		at: [/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/workflow_testsuite.go:409 /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_test.go:656]
        /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_testsuite.go:1208: FAIL:	GetNextWorkflows(string,*activities.GetNextWorkflowsRequest)
            		at: [/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/workflow_testsuite.go:409 /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_test.go:664]
        /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_testsuite.go:1208: FAIL:	BeganWorkflow(string,mock.argumentMatcher)
            		at: [/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/workflow_testsuite.go:409 /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_test.go:685]
        /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_testsuite.go:1208: FAIL:	CompletedWorkflow(string,mock.argumentMatcher)
            		at: [/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/workflow_testsuite.go:409 /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_test.go:695]
        /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_testsuite.go:1208: FAIL:	GetNextWorkflows(string,*activities.GetNextWorkflowsRequest)
            		at: [/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/workflow_testsuite.go:409 /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_test.go:705]
        /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_testsuite.go:1208: FAIL:	SetActivatedDate(string,mock.argumentMatcher)
            		at: [/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/workflow_testsuite.go:409 /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_test.go:719]
        /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_testsuite.go:1208: FAIL: 2 out of 10 expectation(s) were met.
            	The code you are testing needs to make 8 more call(s).
            	at: [/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/workflow_testsuite.go:1208 /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_test.go:67 /Users/johansvedlundnordstrom/go/pkg/mod/github.com/stretchr/testify@v1.10.0/suite/suite.go:180 /opt/homebrew/Cellar/go/1.24.1/libexec/src/runtime/panic.go:792 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow.go:773 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/workflow.go:1467 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow.go:1020 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/context.go:336 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/context.go:206 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow.go:590 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow_testsuite.go:2269 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow_testsuite.go:2880 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow_testsuite.go:880 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow_testsuite.go:820 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow_testsuite.go:610 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow_testsuite.go:554 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/workflow_testsuite.go:820 /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_test.go:739]
        /Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/internal_workflow.go:773: test panicked: getState: illegal access from outside of workflow context
            goroutine 36 [running]:
            runtime/debug.Stack()
            	/opt/homebrew/Cellar/go/1.24.1/libexec/src/runtime/debug/stack.go:26 +0x64
            github.com/stretchr/testify/suite.failOnPanic(0x14000475c00, {0x1057dd4e0, 0x105a2ec40})
            	/Users/johansvedlundnordstrom/go/pkg/mod/github.com/stretchr/testify@v1.10.0/suite/suite.go:89 +0x38
            github.com/stretchr/testify/suite.Run.func1.1()
            	/Users/johansvedlundnordstrom/go/pkg/mod/github.com/stretchr/testify@v1.10.0/suite/suite.go:188 +0x22c
            panic({0x1057dd4e0?, 0x105a2ec40?})
            	/opt/homebrew/Cellar/go/1.24.1/libexec/src/runtime/panic.go:792 +0x124
            go.temporal.io/sdk/internal.assertNotInReadOnlyStateCancellation({0x105a41280?, 0x140000b17a0?})
            	/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow.go:773 +0xa4
            go.temporal.io/sdk/internal.(*workflowEnvironmentInterceptor).NewTimerWithOptions.func2({0x14000180570?, 0x1053b8098?}, 0xc8?)
            	/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/workflow.go:1467 +0x44
            go.temporal.io/sdk/internal.(*channelImpl).Close(0x14000465c60?)
            	/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow.go:1020 +0xb4
            go.temporal.io/sdk/internal.(*cancelCtx).cancel(0x14000465c20, 0x1, {0x105a32660, 0x140003ce7b0})
            	/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/context.go:336 +0xb0
            go.temporal.io/sdk/internal.WithCancel.func1()
            	/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/context.go:206 +0x34
            go.temporal.io/sdk/internal.(*syncWorkflowDefinition).Execute.func2()
            	/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow.go:590 +0x28
            go.temporal.io/sdk/internal.(*testWorkflowEnvironmentImpl).RequestCancelExternalWorkflow(0x140004998c8, {0x1055f0ecd, 0x16}, {0x1400003e330, 0x15}, {0x14000044400, 0x1b}, 0x1400000c078)
            	/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow_testsuite.go:2269 +0x2c8
            go.temporal.io/sdk/internal.(*testWorkflowEnvironmentImpl).RequestCancelExternalWorkflow.(*testWorkflowEnvironmentImpl).cancelWorkflow.(*testWorkflowEnvironmentImpl).cancelWorkflowByID.func4()
            	/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow_testsuite.go:2880 +0x40
            go.temporal.io/sdk/internal.(*testCallbackHandle).processCallback(0x14000059358)
            	/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow_testsuite.go:880 +0xc4
            go.temporal.io/sdk/internal.(*testWorkflowEnvironmentImpl).startMainLoop(0x14000498008)
            	/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow_testsuite.go:820 +0x110
            go.temporal.io/sdk/internal.(*testWorkflowEnvironmentImpl).executeWorkflowInternal(0x14000498008, 0x0, {0x105c6fd7b, 0x14}, 0x0)
            	/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow_testsuite.go:610 +0x3a8
            go.temporal.io/sdk/internal.(*testWorkflowEnvironmentImpl).executeWorkflow(0x14000498008, {0x1057fee40, 0x105a29d20}, {0x0, 0x0, 0x0})
            	/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/internal_workflow_testsuite.go:554 +0x118
            go.temporal.io/sdk/internal.(*TestWorkflowEnvironment).ExecuteWorkflow(0x1400047a880?, {0x1057fee40?, 0x105a29d20?}, {0x0?, 0x2?, 0x2?})
            	/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.32.1/internal/workflow_testsuite.go:820 +0xb8
            go.vxfiber.dev/subscription/subscription/workflows.(*WorkflowTestSuite).Test_Activate_Subscription_Workflow_Spam(0x14000206b40)
            	/Users/johansvedlundnordstrom/dev/v2/subscription/subscription/workflows/workflow_test.go:739 +0xf18
            reflect.Value.call({0x140003fd680?, 0x140001813b0?, 0x1400049c060?}, {0x1055de9d3, 0x4}, {0x140000a8f20, 0x1, 0x105575c94?})
            	/opt/homebrew/Cellar/go/1.24.1/libexec/src/reflect/value.go:584 +0x978
            reflect.Value.Call({0x140003fd680?, 0x140001813b0?, 0x66a?}, {0x140000a8f20?, 0x105f7c978?, 0x1062041a0?})
            	/opt/homebrew/Cellar/go/1.24.1/libexec/src/reflect/value.go:368 +0x94
            github.com/stretchr/testify/suite.Run.func1(0x14000475c00)
            	/Users/johansvedlundnordstrom/go/pkg/mod/github.com/stretchr/testify@v1.10.0/suite/suite.go:202 +0x394
            testing.tRunner(0x14000475c00, 0x14000206c60)
            	/opt/homebrew/Cellar/go/1.24.1/libexec/src/testing/testing.go:1792 +0xe4
            created by testing.(*T).Run in goroutine 35
            	/opt/homebrew/Cellar/go/1.24.1/libexec/src/testing/testing.go:1851 +0x374
FAIL
exit status 1

it fails here..


maybe its just me not setting up mocks properly idk, seems very strange this

seems to be when a timer in the childworkflow is cancelled

nvm also happens in


if i make the childworkflow not reach the time before the cancel happens

so basically for some reason when running in test it thinks the RequestCancelExternalWorkflow in


runs outside of workflow scope

ill try to reproduce with a minimal example

I was able to reproduce the error with a minimal example, here goes

ERROR:


2025/05/29 11:13:56 INFO  Received activation signal
2025/05/29 11:13:56 INFO  ExecuteChildWorkflow WorkflowType ActivationWorkflow
2025/05/29 11:13:56 INFO  Activation workflow started
2025/05/29 11:13:56 DEBUG Auto fire timer TimerID 0 TimerDuration 1s TimeSkipped 1s
2025/05/29 11:13:56 INFO  Received activation signal
2025/05/29 11:13:56 INFO  ExecuteChildWorkflow WorkflowType ActivationWorkflow
--- FAIL: Test_Subscription_Workflow_Tests (0.00s)
    --- FAIL: Test_Subscription_Workflow_Tests/Test_ExampleWorkflow (0.00s)
        /Users/johansvedlundnordstrom/dev/reproduce/workflow_testsuite.go:1236: FAIL:	DummyActivity(string,workflows.DummyActivityInput)
            		at: [/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.34.0/internal/workflow_testsuite.go:414 /Users/johansvedlundnordstrom/dev/reproduce/some_test.go:110]
        /Users/johansvedlundnordstrom/dev/reproduce/workflow_testsuite.go:1236: FAIL: 0 out of 1 expectation(s) were met.
            	The code you are testing needs to make 1 more call(s).
            	at: [/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.34.0/internal/workflow_testsuite.go:1236 /Users/johansvedlundnordstrom/dev/reproduce/some_test.go:27 /Users/johansvedlundnordstrom/go/pkg/mod/github.com/stretchr/testify@v1.10.0/suite/suite.go:180 /opt/homebrew/Cellar/go/1.24.1/libexec/src/runtime/panic.go:792 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.34.0/internal/internal_workflow.go:782 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.34.0/internal/workflow.go:1490 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.34.0/internal/internal_workflow.go:1029 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.34.0/internal/context.go:336 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.34.0/internal/context.go:206 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.34.0/internal/internal_workflow.go:593 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.34.0/internal/internal_workflow_testsuite.go:2288 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.34.0/internal/internal_workflow_testsuite.go:2921 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.34.0/internal/internal_workflow_testsuite.go:896 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.34.0/internal/internal_workflow_testsuite.go:836 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.34.0/internal/internal_workflow_testsuite.go:626 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.34.0/internal/internal_workflow_testsuite.go:570 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/sdk@v1.34.0/internal/workflow_testsuite.go:832 /Users/johansvedlundnordstrom/dev/reproduce/some_test.go:119]
        /Users/johansvedlundnordstrom/dev/reproduce/internal_workflow.go:782: test panicked: getState: illegal access from outside of workflow context

CODE:

package workflows

import (
	"context"
	"errors"
	"testing"
	"time"

	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/suite"
	"go.temporal.io/sdk/testsuite"
	"go.temporal.io/sdk/workflow"
)

type WorkflowTestSuite struct {
	suite.Suite
	testsuite.WorkflowTestSuite

	testEnv *testsuite.TestWorkflowEnvironment
}

func (s *WorkflowTestSuite) SetupTest() {
	s.testEnv = s.NewTestWorkflowEnvironment()
}

func (s *WorkflowTestSuite) AfterTest(suiteName, testName string) {
	s.testEnv.AssertExpectations(s.T())
}

func Test_Subscription_Workflow_Tests(t *testing.T) {
	suite.Run(t, new(WorkflowTestSuite))
}

func MyCoolWorkflow(ctx workflow.Context) error {
	selector := workflow.NewSelector(ctx)
	var activationWorkflow *workflow.Execution
	selector.AddReceive(workflow.GetSignalChannel(ctx, "activate"), func(c workflow.ReceiveChannel, more bool) {
		c.Receive(ctx, nil)
		workflow.GetLogger(ctx).Info("Received activation signal")
		if activationWorkflow != nil {
			workflow.RequestCancelExternalWorkflow(ctx, activationWorkflow.ID, activationWorkflow.RunID)
		}

		cwf := workflow.ExecuteChildWorkflow(
			ctx,
			ActivationWorkflow,
		)

		var res workflow.Execution
		if err := cwf.GetChildWorkflowExecution().Get(ctx, &res); err != nil {
			workflow.GetLogger(ctx).Error("Failed to start child workflow", "error", err)
			return
		}
		activationWorkflow = &res

		selector.AddFuture(cwf, func(f workflow.Future) {
			if err := f.Get(ctx, nil); err != nil {
				workflow.GetLogger(ctx).Error("Child workflow failed", "error", err)
			} else {
				workflow.GetLogger(ctx).Info("Child workflow completed successfully")
			}
			activationWorkflow = nil
		})
	})

	for selector.HasPending() || activationWorkflow != nil {
		selector.Select(ctx)
	}
	return nil
}

type DummyActivityInput struct{}

func DummyActivity(ctx context.Context, d DummyActivityInput) error {
	return nil
}

func ActivationWorkflow(ctx workflow.Context) error {
	ctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
		StartToCloseTimeout: time.Minute,
	})
	workflow.GetLogger(ctx).Info("Activation workflow started")
	if err := workflow.Sleep(ctx, time.Hour); err != nil {
		workflow.GetLogger(ctx).Error("Activation workflow failed", "error", err)
		return err
	}
	workflow.GetLogger(ctx).Info("Activation workflow completed")

	var err error
	if errors.Is(ctx.Err(), workflow.ErrCanceled) {
		newCtx, _ := workflow.NewDisconnectedContext(ctx)
		err = workflow.ExecuteActivity(newCtx, DummyActivity, DummyActivityInput{}).Get(newCtx, nil)
		workflow.GetLogger(ctx).Info("Activation workflow canceled, running dummy activity in disconnected context")
	} else {
		err = workflow.ExecuteActivity(ctx, DummyActivity, DummyActivityInput{}).Get(ctx, nil)
	}

	return err
}

func (s *WorkflowTestSuite) Test_ExampleWorkflow() {
	s.testEnv.OnActivity(DummyActivity, mock.Anything, DummyActivityInput{}).Return(nil).Once()
	s.testEnv.RegisterWorkflow(ActivationWorkflow)

	s.testEnv.RegisterDelayedCallback(func() {
		s.testEnv.SignalWorkflow("activate", nil)
	}, 0)
	s.testEnv.RegisterDelayedCallback(func() {
		s.testEnv.SignalWorkflow("activate", nil)
	}, time.Second)
	s.testEnv.ExecuteWorkflow(MyCoolWorkflow)
	s.NoError(s.testEnv.GetWorkflowError())
	s.testEnv.IsWorkflowCompleted()
}```

created an issue for it Getting a strange error when writing tests which does not occur in real temporal deployment · Issue #1961 · temporalio/sdk-go · GitHub