Dapr 1.17.1
This update includes bug fixes:
- WASM binding and middleware components fail to register on non-wasm architectures
- Unstalled workflows cannot be deleted by state retention policy
- Unnecessary placement dissemination for daprd hosts with no actor types
- Bulk subscription timer not reset after early dispatch due to maxMessagesCount
WASM binding and middleware components fail to register on non-wasm architectures
Problem
After upgrading to Dapr v1.16.0 or later, applications using the WASM output binding or WASM HTTP middleware component fail to start with the error:
FATA[0000] Fatal error from runtime: process component wasm error:
[INIT_COMPONENT_FAILURE]: initialization error occurred for wasm
(bindings.wasm/v1): couldn't find binding wasm (bindings.wasm/v1)
Impact
WASM binding and WASM HTTP middleware components are completely unavailable on all production architectures (amd64, arm64, arm), affecting all versions from v1.16.0 through v1.17.0.
Root Cause
In v1.16.0, PR [#9009] renamed component registration files for naming consistency:
binding_webassembly.go→bindings_wasm.gomiddleware_http_webassembly.go→middleware_http_wasm.go
Go applies an implicit build constraint when a filename matches *_GOARCH.go. Since wasm is a valid GOARCH value, files ending in _wasm.go are only compiled when GOARCH=wasm, causing the WASM components to never be registered on amd64/arm64/arm platforms. This inadvertently reverted fixes from [#5486] and [#6439] which specifically addressed this exact filename clash.
Solution
Renamed the files back to use the _webassembly suffix which does not match any valid GOARCH value:
bindings_wasm.go→bindings_webassembly.gomiddleware_http_wasm.go→middleware_http_webassembly.go
Unstalled workflows cannot be deleted by state retention policy
Problem
When a stalled workflow became unstalled (e.g. by re-registering the original workflow version), the completed workflow instance could not be deleted by the state retention policy or the purge API.
Impact
Workflow instances that were previously stalled and then unstalled would remain in the state store indefinitely, even when a state retention policy was configured to clean up completed workflows.
Root Cause
The workflow runtime metadata state tracker did not remove an internal Stalled tracker once a workflow became unstalled.
This prevented purge API calls from completing, even when the workflow was in a COMPLETED state.
Solution
The workflow runtime metadata state tracker now removes the internal Stalled tracker when a workflow becomes unstalled, allowing API calls to complete successfully and enabling the state retention policy to clean up completed workflows as expected.
Unnecessary placement dissemination for daprd hosts with no actor types
Problem
When a daprd sidecar with no actor types connected to or disconnected from the placement service, it triggered a full placement table dissemination across all connected sidecars. In clusters with many sidecars that do not host actors, this caused unnecessary dissemination cycles that degraded placement performance.
Impact
Every daprd connection and disconnection—even from sidecars with no actor types—triggered a full 3-stage lock dissemination cycle (LOCK → UPDATE → UNLOCK) across all connected sidecars. In large clusters where many sidecars do not host actors, this created significant overhead on the placement service and caused unnecessary locking of actor invocations on all sidecars during each cycle.
Root Cause
The placement service stored all connected hosts in its dissemination table regardless of whether they reported any actor types (entities). Both connecting and disconnecting these hosts triggered a cluster-wide dissemination cycle to propagate the change, even though the host was never meaningful to actor routing.
Solution
The placement service no longer stores hosts with no actor types in the dissemination table, and neither their connection nor disconnection triggers a cluster-wide dissemination. Instead, when a no-types host connects, the placement service sends the current placement table directly to that single stream via a one-shot LOCK/UPDATE/UNLOCK sequence, without involving any other connected sidecars. This ensures that sidecars without actor types still receive the placement table for routing actor invocations, while avoiding the destructive cluster-wide lock cycle.
Bulk subscription timer not reset after early dispatch due to maxMessagesCount
Problem
When using bulk subscriptions with both maxMessagesCount and awaitDuration configured, after a batch was dispatched early because maxMessagesCount was reached, Dapr continued to honor the original timer. The subscriber endpoint was triggered again when the initial awaitDuration elapsed, even if the next batch had not yet accumulated enough messages. This resulted in unnecessary batch triggers with partially filled batches.
Impact
Applications using bulk subscriptions with maxMessagesCount and awaitDuration could receive additional, partially filled batches immediately after a full batch was dispatched. This caused unnecessary processing overhead and potentially unexpected behavior for subscribers expecting to accumulate messages for the full awaitDuration window.
Root Cause
After a count-based flush in processBulkMessages (pkg/runtime/pubsub/default_bulksub.go), the awaitDuration ticker was not reset. The ticker continued from its original start time, so the next tick fired sooner than expected—potentially immediately—rather than waiting the full awaitDuration from the time of the early dispatch.
Solution
Added ticker.Reset() in processBulkMessages after a count-based flush, ensuring the awaitDuration ticker restarts from zero after each early dispatch. The next batch now gets the full duration window to accumulate messages before being dispatched.