Dapr 1.18.2
This update contains the following bug fixes:
- SPIFFE SVID component operation propagation
- MCPServers with secret-referenced credentials reload every 60 seconds
- Actor reminders and jobs fail to register when their name or actor ID contains characters such as
|or@ - IAM Roles Anywhere X.509 authentication breaks with certificate chains, SVID refresh, and missing SVIDs
- Redis components updated to go-redis v9.21.0
- Kafka components lack connection timeouts, a broker health check, and tunable producer configuration
- Sidecars restart when an unrelated Configuration is created or updated
- Sidecar reserves its internal gRPC port before initializing components
- A slow actor timer callback delays timers for every other actor
- Reusing a workflow instance ID while child workflows from the previous execution are still running
- gRPC streaming actor applications could not host actors without opening an app port
- go-chi updated to v5.2.4 for CVE-2025-69725
- mongo-driver updated to v1.17.7 for CVE-2026-2303
- workflow execution metrics status for terminated workflows reported as failed
- Sub-millisecond latencies are omitted from latency metrics
- Parent workflow becomes stuck when creating a child workflow with an instance ID that is already in use
- Querying workflow instance history crashes the sidecar when no actor state store is configured
- Terminating a running workflow could leave it permanently stuck in RUNNING
SPIFFE SVID component operation propagation
Problem
The SPIFFE SVID source was not propagated to component operation calls. As a result, any component that wanted to use the SPIFFE ID for authentication had to capture the source in its init method and manage it explicitly. SDKs such as the Azure SDK, which can transparently use the SPIFFE SVID source when it is present on the context, were unable to do so.
Impact
Azure components could not leverage the SPIFFE ID for authentication without explicitly handling the source, and this also blocked future support for SPIFFE-based authentication in other components.
Root Cause
The SPIFFE SVID source was attached only to the component's init method context, not to the context passed into operation calls.
Solution
The SPIFFE SVID source is now attached to the context passed into operation calls, allowing components to use the SPIFFE ID for authentication on a per-operation basis.
MCPServers with secret-referenced credentials reload every 60 seconds
Problem
An MCPServer whose transport credentials come from a secret (a secretKeyRef or envRef in spec.endpoint.streamableHTTP.headers, spec.endpoint.sse.headers, or spec.endpoint.stdio.env) was closed and reloaded roughly every 60 seconds, even when nothing about the resource had changed.
Impact
You were affected if you ran one or more MCPServers whose headers or stdio environment referenced a secret. Plain MCPServers with no secret references were not affected.
Note: auth.oauth2.secretKeyRef is not affected. The OAuth2 client secret is fetched at connection time and never written into the spec, so it never took part in the comparison and did not churn.
Root Cause
Alongside the event-driven watch, the hot-reload reconciler runs a periodic backup reconcile (about every 60 seconds) that lists resources from the control plane and compares them against the copy the sidecar currently has loaded. The loaded copy has its secret references resolved to their values, but the incoming copy was compared without resolving its secret references first. For any secret-backed MCPServer the resolved value never matched the unresolved reference, so the comparison always reported a difference and the server was reloaded on every cycle.
Solution
The MCPServer reconciler now resolves the incoming resource's secret references before comparing it against the loaded copy, matching the behavior already used for components. An unchanged secret-backed MCPServer now compares equal and is left running, while a genuine change, including a rotated secret value, still triggers a reload.
Actor reminders and jobs fail to register when their name or actor ID contains characters such as | or @
Problem
Registering an actor reminder through the Scheduler service failed when the reminder name, or the actor ID it belongs to, contained certain characters such as the pipe | or at sign @.
The same characters are accepted when invoking actors and when saving actor state, so an actor that worked everywhere else could not have a reminder created for it.
The error returned in this case was also misleading:
a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')
It claimed only lowercase names were allowed even though uppercase names are in fact accepted, and it did not describe which characters were actually rejected.
Impact
You were affected if you used Scheduler-backed actor reminders (the default since 1.15) and your reminder names, actor IDs, or scheduled job names contained characters outside the strict DNS-1123 set, for example |, @, or uppercase letters.
Root Cause
The Scheduler composes each reminder or job into a single name of the form actorreminder||<namespace>||<type>||<id>||<name> (or app||<namespace>||<appID>||<name> for jobs), using || as an internal delimiter.
Each ||-delimited segment was then validated against Kubernetes' DNS-1123 subdomain rules, which only permit lowercase alphanumeric characters, -, and ..
This was far stricter than the character set Dapr already accepts at its API edge, producing the inconsistency between actor invocation and reminder registration.
Because the validator lowercased each segment before checking it, uppercase names passed in practice while the surfaced error still referred to lowercase-only RFC 1123 subdomains.
Solution
The Scheduler now validates names using the same policy Dapr applies at its API edge, so anything accepted for actor invocation can also be used for a reminder or job.
Reminder names, job names, and actor identifiers may now contain any character except /, \, #, ?, control characters (including the NUL byte), and the exact path sequences . and ...
Uppercase letters and characters such as | and @ are allowed, and || continues to be accepted within actor IDs and names.
Listing reminders for actors whose IDs contain || now also reports the correct actor metadata.
Validation errors now describe the characters that are actually disallowed.
IAM Roles Anywhere X.509 authentication breaks with certificate chains, SVID refresh, and missing SVIDs
Problem
The IAM Roles Anywhere X.509 credential provider used by AWS components did not work with realistic SPIFFE / Dapr Sentry PKIs.
It presented only the leaf certificate, so a workload whose leaf is signed by an intermediate CA could not be validated against a trust anchor registered at the chain root.
After the short-lived SPIFFE SVID expired, every credential refresh failed with missing required fields and the component lost AWS access.
When no SPIFFE SVID was present, or the workload private key was not ECDSA, the provider panicked and crashed the entire sidecar.
Impact
You were affected if you used AWS components configured with IAM Roles Anywhere X.509 authentication (trustAnchorArn, trustProfileArn, and assumeRoleArn), particularly with a certificate chain that includes one or more intermediate CAs.
Affected sidecars lost AWS access once the SVID rotated, and a missing or non-ECDSA SVID could crash the sidecar outright.
Root Cause
Only the leaf certificate was sent to IAM Roles Anywhere, omitting the intermediate chain needed to build a path to the registered trust anchor. The refresh path rebuilt the credential provider from region and ARN fields that the constructor never populated, so any refresh after SVID expiry failed validation. The provider also used an unchecked type assertion on the private key and did not guard against a missing SVID, turning both conditions into panics.
Solution
The signer now presents the leaf plus the intermediate chain, which is sent in the X-Amz-X509-Chain header so IAM Roles Anywhere can validate against a trust anchor at the chain root.
Credential refresh now swaps the signer on the existing provider, which already retains the region and profile/anchor/role ARNs from initialization, instead of rebuilding it from unset fields.
The refreshed certificate is stored only after the signer swap succeeds, keeping the certificate and signer in lockstep so a failed refresh keeps retrying instead of silently adopting a stale signer.
A missing SPIFFE SVID and a non-ECDSA workload key now return errors instead of panicking, so a misconfiguration no longer crashes the sidecar.
Redis components updated to go-redis v9.21.0
Problem
The shared Redis client used by Dapr's Redis-backed components was pinned to github.com/redis/go-redis/v9 v9.6.3.
Moving forward to the current v9.21.0, which carries the bug fixes and improvements accumulated across many upstream releases, was blocked by a build break: v9.21.0 adds two fields (MillisElapsedFromDelivery and DeliveredCount) to the go-redis XMessage type, which broke the whole-struct conversion Dapr used to map XClaim results into its own RedisXMessage type.
Impact
You are affected if you use any Redis-backed component, such as the Redis state store, pub/sub, configuration store, or distributed lock. These now run on go-redis v9.21.0 instead of v9.6.3.
Root Cause
XClaimResult converted each go-redis XMessage to the internal RedisXMessage with a whole-struct conversion (RedisXMessage(xMessage)), which is only valid while the two structs have identical fields.
When v9.21.0 added fields to XMessage, that conversion no longer compiled.
Solution
The Redis client dependency is bumped to go-redis v9.21.0, and XClaimResult now assigns the ID and Values fields explicitly, matching the field-by-field pattern already used by XReadGroupResult.
This keeps the conversion resilient to future field additions in the upstream XMessage type.
Kafka components lack connection timeouts, a broker health check, and tunable producer configuration
Problem
The Kafka components (pub/sub and bindings, including the confluent and wurstmeister variants) are built on a shared Kafka component that uses the Sarama client with its default network configuration. The Sarama dial, read, write, and metadata timeouts were not exposed as component metadata, there was no way to probe broker reachability, and the producer's acknowledgement and retry settings were hard-coded. Against unreachable or slow brokers, operations — including component initialization — could block far longer than expected, with no health signal to surface that a broker had become unreachable.
Impact
You were affected if you used the Kafka pub/sub or Kafka binding components. Connectivity problems could cause operations to hang and initialization to block on unreachable brokers, no built-in check existed to detect broker unavailability, and the producer's durability and retry behavior was fixed and could not be tuned.
Root Cause
The shared Kafka component created its Sarama client with Sarama's default configuration and did not surface the Net dial, read, write, and metadata timeouts as component metadata, so callers could not bound how long operations waited on the network. No broker connectivity probe existed, and the producer's RequiredAcks and retry count were hard-coded when constructing the sync producer.
Solution
The shared Kafka component now exposes tunable network timeouts and producer settings through component metadata, and adds a broker health check:
- New network timeout metadata:
dialTimeout,readTimeout, andwriteTimeout(each default 30s), andmetadataTimeout(default 0, meaning Sarama's own default applies). Invalid values fall back to the defaults. - New producer metadata:
producerRequiredAcks(all(default),local, ornone) andproducerRetryMax(default 5), preserving the previous behavior when unset. - A broker connectivity probe that verifies the configured brokers are reachable. It disables metadata retries so it fails fast against an unhealthy cluster, honors the caller's context and timeout, and does not modify component state.
- Component initialization no longer blocks indefinitely on unreachable brokers.
All defaults preserve the previous behavior, so no configuration change is required.
Sidecars restart when an unrelated Configuration is created or updated
Problem
In Kubernetes mode, a Dapr sidecar performed a full runtime restart (a SIGHUP hot reload) whenever any Configuration resource in its namespace was created or updated, including Configuration resources the sidecar does not use. Configuration hot reloading restarts the sidecar by design, but it should only do so for the Configuration that the sidecar is actually running with.
Impact
You were affected if you ran more than one app with different dapr.io/config annotations in the same namespace and created or updated Configuration resources there.
Unrelated apps would restart their sidecars on every such change.
Apps that are the only Dapr app in their namespace, or namespaces with a single Configuration, were effectively unaffected.
Root Cause
The operator streams Configuration updates to sidecars over a per-app stream that filters resources by the requesting app's namespace and scopes. Configuration resources do not carry scopes, so the operator's informer filter treated every Configuration in the namespace as belonging to every app and streamed all of them to every connected sidecar. The sidecar's SIGHUP reconciler then restarted the runtime for Configurations it had never loaded.
Solution
The operator now determines, server side, which Configuration is assigned to a connecting sidecar from its pod's dapr.io/config annotation, keyed off the app identity authenticated over mTLS, and only streams updates for that Configuration.
The sidecar is not trusted to self-report which Configuration is its own, so a sidecar can no longer be made to receive updates for a Configuration that does not belong to it. A sidecar is now restarted only when its own assigned Configuration changes.
The pod lookup uses a dedicated metadata-only cache so the operator does not fetch or retain full pod objects.
Sidecar reserves its internal gRPC port before initializing components
Problem
A Dapr sidecar (daprd) could randomly fail to start with bind: address already in use on its internal gRPC port (default 50002), even though no other process was using it.
Impact
You were affected if your sidecars loaded components that open outbound connections during initialization (Redis-backed state stores, pub/sub, configuration stores, or locks). It was most likely on Linux and got more frequent with more components and more frequent pod restarts.
Root Cause
The sidecar initialized its components before binding its internal gRPC port. Component initialization opens outbound connections, and the OS can assign the internal gRPC port as the connection's ephemeral source port (the default 50002 falls inside the Linux ephemeral range 32768–60999), so the sidecar's later attempt to listen on that port failed.
Solution
The sidecar now binds its internal gRPC port at the very start of runtime initialization, before any component is initialized, and hands that already-bound listener to the internal gRPC server rather than binding the port a second time.
Because the sidecar holds the port from the outset, the operating system will not hand it out as an ephemeral source port, so component connections can no longer take it and the sidecar starts reliably.
If initialization fails before the internal gRPC server starts, the reserved port is released.
No configuration change is required. If you previously set the dapr.io/internal-grpc-port annotation to a value outside the dynamic range (for example 61002) as a workaround, you can keep or remove it.
A slow actor timer callback delays timers for every other actor
Problem
All actor timer callbacks on a sidecar executed one at a time. While one callback was running, no other timer could fire, so a single slow callback delayed the timers of every other actor hosted on that sidecar, and due timers piled up behind it.
Impact
You were affected if you used actor timers and any timer callback took a noticeable amount of time to return. Timers for unrelated actors on the same sidecar fired late by the accumulated duration of the callbacks queued ahead of them.
Root Cause
Actor timers live in the sidecar's memory and are scheduled by a single time-ordered queue. The queue's processing loop invoked each due timer's callback synchronously on its own goroutine, so callbacks executed serially across the entire sidecar regardless of which actor they belonged to.
Solution
Due timers are now routed to a per-actor execution loop. Callbacks for the same actor still run one at a time in scheduled order, and a repeating timer is still re-armed only after its current callback returns, but callbacks for different actors now run concurrently, so a slow callback delays only its own actor's timers. Per-actor loops are created when an actor's first timer fires and are reclaimed once the actor has no registered timers and no outstanding fires. Deleting or replacing a timer now also cancels a fire that is already waiting behind a running callback for the same actor.
Reusing a workflow instance ID while child workflows from the previous execution are still running
Problem
Creating a workflow with the instance ID of a completed, failed, or terminated workflow succeeded even while child workflows started by that previous execution were still running. A still-running child belongs to the old execution but reports its completion to the parent instance ID, so its events could be delivered into the new execution, and a new execution that pins the same child IDs could collide with the old execution's live children.
Impact
You were affected if you reuse deterministic workflow instance IDs for workflows that create child workflows, and recreated a parent whose children had not finished. This arises when a parent completes without awaiting its children, or is terminated without recursion, leaving the children running. Workflows created with fresh or auto-generated instance IDs were not affected, and are not affected by the fix.
Root Cause
The create path only checked the runtime status of the instance being recreated. Child workflows are independent instances that can outlive a terminal parent, and the ones recorded in the previous execution's history were never consulted.
Solution
Recreating a terminal workflow now verifies that every child workflow of the previous execution, checked recursively and across app boundaries, is also in a terminal state or has been purged. If a descendant is still running or cannot be verified, the create request is rejected with a conflict error naming the blocking child workflow; purging the workflow continues to free its instance ID unconditionally. Creates with a fresh instance ID take the same path as before and perform no additional work.
gRPC streaming actor applications could not host actors without opening an app port
Problem
Dapr 1.18 introduced hosting actors over the app-initiated gRPC callback stream (SubscribeActorEventsAlpha1).
A headline property of this feature is that the application dials the sidecar itself and therefore does not need to listen on any port.
In practice, opening the stream against a sidecar started without --app-port failed immediately with a FailedPrecondition error stating that actor callback streaming requires a gRPC app channel.
Applications were forced to open a local port and pass it to the sidecar via --app-port (or the dapr.io/app-port annotation) purely to satisfy the sidecar's channel setup, even though actor traffic never used that port.
Impact
You were affected if you hosted actors over the streaming actor API and ran the sidecar without an app port configured. The callback stream was rejected before registration, so the application's actor types were never registered and no actor traffic could be served.
Root Cause
The sidecar only creates its app channel when an app port is configured. Both the stream endpoint's guard and the actor transport selection obtained the callback stream manager by inspecting the gRPC app channel, so with no app port there was no channel to inspect and the stream was rejected as if the application were HTTP-based.
Solution
The stream endpoint now serves the callback stream from the runtime-owned stream manager when no app channel exists, and actor registration passes that manager explicitly to the actor transport instead of deriving it from the app channel. A sidecar started without an app port now accepts the callback stream, registers the application's actor types, and routes invocations, reminders, timers, and deactivations over the stream, so the application does not need to listen on any port.
go-chi updated to v5.2.4 for CVE-2025-69725
Problem
The Dapr HTTP server depends on github.com/go-chi/chi/v5, which was pinned to v5.2.2. That version is affected by CVE-2025-69725.
Impact
You are affected if you run daprd. Dependency scanners report CVE-2025-69725 against the sidecar binary while it is built against go-chi v5.2.2.
Root Cause
The github.com/go-chi/chi/v5 dependency predated the upstream fix released in v5.2.4.
Solution
github.com/go-chi/chi/v5 is updated to v5.2.4, which resolves CVE-2025-69725. This is a dependency-only change with no behavioral impact.
mongo-driver updated to v1.17.7 for CVE-2026-2303
Problem
The MongoDB Go driver go.mongodb.org/mongo-driver was pinned to v1.14.0, which is affected by CVE-2026-2303. It is used directly by the actor reminder subsystem and by the MongoDB components in components-contrib.
Impact
You are affected if you run daprd, and in particular if you use the MongoDB state store. Dependency scanners report CVE-2026-2303 against the sidecar binary while it is built against mongo-driver v1.14.0.
Root Cause
The go.mongodb.org/mongo-driver dependency predated the upstream fix released in v1.17.7, both in the runtime and in the components-contrib dependency.
Solution
go.mongodb.org/mongo-driver is updated to v1.17.7, and the github.com/dapr/components-contrib dependency is bumped to v1.18.3 to carry the same driver update on the component side. Both resolve CVE-2026-2303. The upgrade stays within the driver's v1 line, so it is a dependency-only change with no behavioral impact.
Sub-millisecond latencies are omitted from latency metrics
Problem
Latency measurements below one millisecond were omitted from latency histograms.
Impact
Operations completing in less than one millisecond were not represented in the affected latency histograms, causing low-latency observations to be undercounted.
Root Cause
ElapsedSince divided time.Duration values before converting the result to float64, truncating sub-millisecond durations to zero. Existing positive-latency guards then skipped those observations.
Solution
ElapsedSince now converts durations to floating-point milliseconds before division, preserving fractional milliseconds and allowing sub-millisecond latency observations to be recorded.
workflow execution metrics status for terminated workflows reported as failed
Problem
The dapr_runtime_workflow_execution_count metric does not distinguish failed workflow executions from deliberately terminated ones and reports both with the failed status label
Impact
Operators are expected to alert on workflow failures using this metric, so status="failed" should mean the workflow failed, excluding those terminated by users.
Root Cause
The workflow runtime has a simple, two ways logic for assigning the status to the metric: if it is not RUNTIME_STATUS_COMPLETED is assumed failed.
Solution
Modify this logic to consider failed and terminated statuses.
Parent workflow becomes stuck when creating a child workflow with an instance ID that is already in use
Problem
A workflow that created a child workflow with an explicit instance ID already belonging to another active workflow never advanced.
The runtime retried creating the child indefinitely, the parent stayed in RUNNING forever, and no error was surfaced to the workflow code.
The sidecar logged a retry warning on every attempt:
Workflow actor 'parent': execution failed with a recoverable error and will be retried later:
'failed to invoke method 'CreateWorkflowInstance' on actor 'child-id':
rpc error: code = AlreadyExists desc = an active workflow with ID 'child-id' already exists'
Impact
You were affected if your workflows create child workflows with deterministic or user-provided instance IDs that can collide with a live workflow instance. The parent workflow hung indefinitely and had to be terminated manually. Child workflows with auto-generated instance IDs were not affected.
Root Cause
When dispatching a child workflow creation, the parent's workflow actor treated every failure as transient and retried it through its wake-up reminder.
The AlreadyExists rejection from the target instance is not transient while the occupying workflow remains active, so the retry loop never succeeded and the failure was never reported back to the awaited child workflow task.
Solution
The child workflow task now fails immediately with an error naming the conflict (an active workflow with ID '<id>' already exists) instead of being retried by the runtime.
The parent advances: workflow code can handle the error and continue, propagate it to fail the parent, or attach a retry policy to the child workflow call, in which case the creation is re-attempted and succeeds once the instance ID becomes free.
The same handling applies when the target ID belongs to a terminal workflow whose child workflow tree is not yet terminal, to cross-app child workflows, and to child workflows re-driven by a workflow rerun.
A detached workflow spawn onto an occupied instance ID is dropped with a warning, matching its fire-and-forget semantics.
The workflow occupying the instance ID is never affected by the rejected creation.
Sub-millisecond latencies are omitted from latency metrics
Problem
Latency measurements below one millisecond were omitted from latency histograms.
Impact
Operations completing in less than one millisecond were not represented in the affected latency histograms, causing low-latency observations to be undercounted.
Root Cause
ElapsedSince divided time.Duration values before converting the result to float64, truncating sub-millisecond durations to zero. Existing positive-latency guards then skipped those observations.
Solution
ElapsedSince now converts durations to floating-point milliseconds before division, preserving fractional milliseconds and allowing sub-millisecond latency observations to be recorded.
Querying workflow instance history crashes the sidecar when no actor state store is configured
Problem
Querying workflow instance history (dapr workflow history, or the GetInstanceHistory call on the workflow gRPC API) against a sidecar whose state store is not an actor state store (no component with actorStateStore: "true") crashed daprd with a nil pointer dereference. The sidecar exited with status 2, taking the application down with it.
Impact
Any 1.18 sidecar without an actor state store that is reachable over the workflow gRPC API could be taken down by a single read-only history query — including queries for instance IDs that do not exist. Other workflow APIs on the same sidecar (such as listing instances) returned a clean error and were not affected.
Root Cause
The workflow read and purge paths in the actor backend (GetInstanceHistory, loadInternalState, purgeWorkflowForce) obtained the actor state from State(), which returns a nil state with no error when no actor state store is configured, and passed it straight to LoadWorkflowState, which dereferenced it. The write path already guarded against this, but the read paths did not.
Solution
The read and purge paths now return the same actionable error the write path already surfaces (the state store is not configured to use the actor runtime. Have you set the - name: actorStateStore value: "true" in your state store component file?) instead of dereferencing a nil state. The history query fails cleanly and the sidecar stays up.
Terminating a running workflow could leave it permanently stuck in RUNNING
Problem
Terminating a running workflow that had fanned out to child workflows could leave the parent workflow permanently stuck in the RUNNING status.
The termination event remained undrained in the workflow's inbox, no ExecutionCompleted event was written to history, and repeated TerminateWorkflow calls could hang.
Restarting the application did not recover the instance; only purging it did.
Impact
You were affected if you terminated running workflows that had child workflows, most likely under load or during transient Scheduler or host unavailability. Affected instances could never be terminated and reported RUNNING forever, even though the runtime logged them as terminated. Termination of sibling child workflows could also be delayed indefinitely while the parent retried.
Root Cause
On a recursive terminate, the workflow engine delivered the termination event to every child workflow before the parent's own terminal state was persisted. If delivery to any child failed, for example an unreachable host or a transient Scheduler error while creating the child's wake-up reminder, the whole operation was rolled back, discarding the parent's computed terminal state, and every retry repeated the same failing sequence. Child delivery also ran while holding the parent workflow actor's lock, which could deadlock against children concurrently reporting their completion back to the parent, causing the terminate call to hang.
Solution
The parent workflow now persists its terminal state and drains its inbox first, then delivers the termination to its children as a separate, idempotent step that is retried until every child is reached. Same-app children are terminated through a durable reminder carrying the termination event, which takes no actor locks and keeps retrying while a child's host is unavailable. Cross-app children continue to be terminated through the existing event delivery method. A failure to reach a child can no longer roll back the parent's terminal state or block its completion.