Dapr 1.18.8
This update contains the following bug fixes and improvements:
- Completed workflows created a reminder for every duplicate result
- Cancelled actor calls broke turn-based concurrency
- Sidecar stopped exporting trace spans after a hot reload restart
- Redis components could not open new connections after the Entra ID token expired
- Security: Compromised eMudhra intermediate CA keys were trusted for outbound TLS
- Sidecar leaked a connection every second while the Scheduler was unreachable
Completed workflows created a reminder for every duplicate result
Problem
When an activity result or external event arrived again for a workflow that had already completed, the sidecar created a wake-up reminder for the workflow. The reminder fired and ran a workflow turn with nothing to process.
Impact
You were affected if you ran workflows whose activities could run more than once, for example after a sidecar restart or a placement change. After a fan-out workflow completed, each late duplicate result cost a reminder create in the Scheduler, a reminder fire and an actor turn.
Root Cause
The sidecar drops a result that is already in the workflow's history or inbox, and creates the wake-up reminder again in case the first one was lost. It did this for completed workflows too, although a completed workflow never processes its inbox again.
Solution
The sidecar acknowledges a duplicate result for a completed workflow without creating a reminder. A running workflow still gets its wake-up reminder.
Cancelled actor calls broke turn-based concurrency
Problem
When a caller cancelled an actor call that was waiting for the actor's current turn to finish, the sidecar could send other waiting calls to the app while that turn was still running. Several calls could then run at once on the same actor ID, with reentrancy enabled or disabled.
Impact
You were affected if callers of your actors cancel calls, for example through client timeouts or abandoned requests, while other calls to the same actor are waiting. Turns that read, modify and write actor state could run at the same time and overwrite each other's changes. Removing an actor timer while its call was waiting behind a running turn caused the same problem.
Root Cause
Each actor keeps a queue of calls waiting for its lock, and the running turn is at the front of that queue. When a waiting call was cancelled, it removed the front of the queue, which was the running turn, rather than its own entry. The next waiting call then started while the running turn was still executing.
Solution
Only the running turn removes calls from the queue. A call cancelled while waiting is marked as abandoned and skipped when the running turn finishes, so each actor runs one turn at a time again.
Sidecar stopped exporting trace spans after a hot reload restart
Problem
After hot reload restarted the sidecar's runtime, the sidecar exported no trace spans for as long as the process ran. Nothing was logged. Requests still succeeded and the sidecar still propagated trace context, so the application's spans named parent spans that never reached the tracing backend.
Impact
You were affected if you export traces with hot reload enabled, which is the default since 1.18.0. Hot reload restarts the runtime when a Configuration, HTTPEndpoint or Resiliency resource changes. In self-hosted mode, any file change in a resources directory that holds a Configuration also restarted it. Recreating the sidecar restored span export until the next restart. Versions 1.17 and earlier are not affected, because hot reload did not restart the runtime.
Root Cause
The sidecar and its workflow engine each looked up their tracer once, when the process started. OpenTelemetry binds those tracers to the first tracer provider the runtime registers. A hot reload restart shut that provider down and registered a new one, but the tracers stayed bound to the old provider, which had no exporter left.
Solution
The sidecar registers one tracer provider for the life of the process. It forwards every span to the tracer provider of the current runtime, so tracers looked up at startup keep exporting after a restart.
Redis components could not open new connections after the Entra ID token expired
Problem
With useEntraID: true, the Redis state store, pub/sub, binding, lock and configuration components worked until the first access token expired. After that, every new connection to Redis failed with WRONGPASS invalid username-password pair or user is disabled, and the component stayed broken until the sidecar was restarted.
Impact
You were affected if a Redis component authenticated to Azure Cache for Redis or Azure Managed Redis with Microsoft Entra ID. The first token lasts about an hour for a service principal and up to 24 hours for a managed identity. Once it expired, any connection the client opened afterwards failed to authenticate, whether the pool replaced an idle or aged connection, recovered from a network interruption, or grew under load. Reporters saw every call through the component fail after the first token's lifetime, until the pod was restarted.
Root Cause
At startup the component fetched one token and stored it as the connection password. The Redis client sends that stored password every time it opens a connection, so a connection opened after the token expired presented a token Redis no longer accepted. A background routine refreshed the token before it expired, but it only re-authenticated the single connection it ran on and never replaced the stored password.
Solution
The component fetches a current token every time it opens a Redis connection, so connections opened after the first token expires authenticate with a valid one.
Security: Compromised eMudhra intermediate CA keys were trusted for outbound TLS
Problem
The private keys of eight eMudhra intermediate CA certificates were compromised and reported to the browser root programs on October 17, 2026. Anyone holding one of these keys can issue a TLS certificate for any domain that Go programs accept as valid. Dapr 1.18.7 and earlier were built with Go 1.26.9, which still trusts certificate chains through these intermediates.
Impact
You were affected if daprd makes outbound TLS connections that are verified against the system trust store, such as components connecting to cloud services or service invocation to external HTTPEndpoints. An attacker holding one of the keys and able to intercept that traffic could impersonate the remote service and read or change the data sent to it. mTLS between sidecars and the control plane uses Dapr's own CA and is not affected. According to the Chrome Root Program, no website certificate chaining to these intermediates had been logged to Certificate Transparency before they were blocked.
Root Cause
Go's crypto/x509 verifier does no revocation checks, so the compromised intermediates stayed trusted until Go itself blocked them.
Solution
Dapr is now built with Go 1.26.10, whose crypto/x509 package rejects any certificate chain that passes through the compromised keys.
Sidecar leaked a connection every second while the Scheduler was unreachable
Problem
While the sidecar could not reach any Scheduler host, it opened a new gRPC connection for each attempt to watch the Scheduler host list, about once a second, and never closed the connection when the attempt failed. Each leaked connection kept its goroutines and memory until the sidecar restarted.
Impact
You were affected if the Scheduler was unreachable from a sidecar for a sustained period, for example because its replicas were crash looping or their etcd volumes had run out of disk. The goroutine count and memory use of every sidecar grew for as long as the outage lasted. In the reported case, each sidecar accumulated several thousand leaked goroutines over two and a half days. Restarting the sidecar released them.
Root Cause
The sidecar opens a connection to a Scheduler host and then starts the WatchHosts stream on it. When starting the stream failed, the sidecar returned the error without closing the connection it had just opened, and the retry loop opened another one a second later.
Solution
The sidecar closes the connection when starting the WatchHosts stream fails, so each failed attempt releases its connection before the next one.