getWorkflowState() stuck at RUNNING despite workflow completion (state sync bug)
- Dominant language
- JavaScript
- Stars
- 217
- Forks
- 104
- PR merge metrics
- No merged PRs in 30d
Description
# 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](https://github.com/user-attachments/files/25282408/dapr-whenanybug-repro.zip)
```bash
cd dapr-whenanybug-repro
```
2. Deploy to Kubernetes cluster with Dapr installed:
```bash
# 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:
```bash
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
```javascript
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.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.