Skip to content

getWorkflowState() stuck at RUNNING despite workflow completion (state sync bug) #734

@leblancmeneses

Description

@leblancmeneses

Dapr Workflow State Not Syncing After whenAny Resolution

Expected Behavior

When a workflow completes after whenAny([waitForExternalEvent, createTimer]) resolves, getWorkflowState() should return COMPLETED status within 1-2 seconds.

Actual Behavior

The workflow completes internally (Dapr logs show Orchestration completed with status COMPLETED), but getWorkflowState() continues returning RUNNING indefinitely.

Steps to Reproduce the Problem

  1. Clone reproduction repository: dapr-whenanybug-repro.zip

    cd dapr-whenanybug-repro
  2. Deploy to Kubernetes cluster with Dapr installed:

    # Build and load image
    TAG="test-$(date +%Y%m%d-%H%M%S)"
    docker build -t localhost:5001/dapr-whenanybug-integrated:${TAG} .
    kind load docker-image localhost:5001/dapr-whenanybug-integrated:${TAG}
    
    # Update deployment with image tag
    sed -i "s|image: localhost:5001/dapr-whenanybug-integrated:.*|image: localhost:5001/dapr-whenanybug-integrated:${TAG}|" k8s/deployment.yaml
    
    # Deploy
    kubectl apply -f k8s/rbac.yaml
    kubectl apply -f k8s/service.yaml
    kubectl apply -f k8s/deployment.yaml
  3. Watch the logs:

    kubectl logs -f -l app=whenanybug-integrated -c integrated
  4. Observe output:

    • ✅ K8s job created by activity
    • ✅ Job calls HTTP endpoint which raises event
    • ✅ Workflow logs: [Workflow] whenAny resolved!
    • ✅ Dapr logs: Orchestration completed with status COMPLETED
    • ❌ Status check: getWorkflowState() returns RUNNING for 10+ seconds

Environment

  • Dapr version: 1.16.8
  • @dapr/dapr version: 3.3.0
  • Kubernetes version: 1.32
  • State store: PostgreSQL with actorStateStore: "true"

Code Pattern

async function* jobWaitWorkflow(ctx, input) {
  // Call activity to create K8s job
  const result = yield ctx.callActivity('createJob', jobName);

  // Wait for external event OR timeout
  const eventTask = ctx.waitForExternalEvent(`job-done:${jobName}`);
  const timerTask = ctx.createTimer(30);
  const winner = yield ctx.whenAny([eventTask, timerTask]);

  // BUG: Workflow completes here but state never syncs
  return winner === eventTask ? 'completed' : 'timeout';
}

The K8s job calls back via HTTP, which raises the event using client.raiseEvent(). The workflow resolves internally but client.getWorkflowState() never reflects the completion.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions