发布日期: 2026-07-21
版本号: v1.18.2

Dapr 1.18.2 版本是一个专注于错误修复的更新,解决了多个组件和子系统中的关键问题。主要修复包括:修复了SPIFFE SVID源未在组件操作调用中传播导致Azure组件无法使用SPIFFE ID进行认证的问题;解决了引用了密钥的MCPServer每隔约60秒被错误关闭并重新加载的问题;修复了提醒名称或Actor ID中包含|@等字符时Actor提醒和作业注册失败的问题;修复了AWS IAM Roles Anywhere的X.509认证在证书链、SVID刷新和SVID缺失场景下的中断问题;将Redis客户端依赖升级至go-redis v9.21.0以修复构建中断并带来上游改进;为Kafka组件增加了连接超时、代理健康检查以及可调的生产者配置;解决了不相关的Configuration资源创建或更新导致Sidecar不必要重启的问题;修复了Sidecar可能在初始化组件之前未预留其内部gRPC端口而导致启动失败的问题;改进了Actor计时器回调的调度,使得一个缓慢的回调不再阻塞同一Sidecar上其他Actor的计时器;修复了重用工作流实例ID时,若前一次执行的子工作流仍在运行可能导致的冲突问题;解决了gRPC流式Actor应用在不开放应用端口的情况下无法托管Actor的问题;更新了go-chi和mongo-driver依赖以修复CVE安全漏洞;修复了已终止工作流的执行指标状态被错误报告为“失败”而非“终止”的问题;修正了亚毫秒级延迟从延迟指标中被忽略的错误;解决了在未配置Actor状态存储时查询工作流实例历史记录会导致Sidecar崩溃的问题;修复了终止正在运行的、包含子工作流的父工作流可能导致其永久卡在“RUNNING”状态的问题。

更新内容 (中文)

Dapr 1.18.2

此更新包含以下错误修复:

SPIFFE SVID 组件操作传播

问题

SPIFFE SVID 源未传播至组件操作调用。因此,任何想要使用 SPIFFE ID 进行身份验证的组件都必须在其 init 方法中捕获该源并显式管理它。诸如 Azure SDK 之类的 SDK(可以在存在 SPIFFE SVID 源时透明地使用它)无法做到这一点。

影响

如果没有显式处理该源,Azure 组件就无法利用 SPIFFE ID 进行身份验证,这也阻碍了其他组件未来对基于 SPIFFE 的身份验证的支持。

根本原因

SPIFFE SVID 源仅附加到组件的 init 方法上下文,而未附加到传入操作调用的上下文中。

解决方案

现在,SPIFFE SVID 源已附加到传入操作调用的上下文中,允许组件按需使用 SPIFFE ID 进行身份验证。

使用密钥引用凭据的 MCPServers 每 60 秒重新加载

问题

传输凭据来自密钥(在 spec.endpoint.streamableHTTP.headersspec.endpoint.sse.headersspec.endpoint.stdio.env 中的 secretKeyRefenvRef)的 MCPServer 会被关闭并大约每 60 秒重新加载一次,即使资源没有任何变化。

影响

如果你运行的一个或多个 MCPServer 的头部或 stdio 环境引用了密钥,则会受到影响。 没有密钥引用的普通 MCPServer 不受影响。

注意:auth.oauth2.secretKeyRef 不受影响。 OAuth2 客户端密钥在连接时获取,永远不会写入 spec,因此它从未参与比较,也没有因此产生波动。

根本原因

除了事件驱动的 watch,热重载协调器还会运行一个周期性备份协调(大约每 60 秒),从控制面列出资源,并将其与 Sidecar 当前加载的副本进行比较。 加载的副本已将其密钥引用解析为具体值,但传入的副本在比较时未先解析其密钥引用。 对于任何由密钥支持的 MCPServer,解析后的值永远无法与未解析的引用匹配,因此比较总是报告差异,服务器在每个周期都会重新加载。

解决方案

MCPServer 协调器现在在与加载副本进行比较之前解析传入资源的密钥引用,与组件已使用的功能保持一致。 现在,未更改的密钥支持的 MCPServer 会比较相等并保持运行,而真正的更改(包括轮换的密钥值)仍会触发重新加载。

当 Actor 提醒或作业的名称或 Actor ID 包含如 |@ 等字符时注册失败

问题

当提醒名称或其所属的 Actor ID 包含某些字符(如管道 |@)时,通过 Scheduler 服务注册 Actor 提醒会失败。 这些字符在调用 Actor 和保存 Actor 状态时是被接受的,因此在其他地方都能正常工作的 Actor 无法为其创建提醒。

此情况下返回的错误也具有误导性:

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])?)*')

它声称只允许小写名称,而实际上大写名称是被接受的,并且没有描述实际被拒绝的是哪些字符。

影响

如果你使用 Scheduler 支持的 Actor 提醒(自 1.15 起为默认值),且你的提醒名称、Actor ID 或计划作业名称包含严格 DNS-1123 集之外的字符,例如 |@ 或大写字母,则会受到影响。

根本原因

Scheduler 将每个提醒或作业组合成一个单一名称,格式为 actorreminder||<namespace>||<type>||<id>||<name>(或作业的 app||<namespace>||<appID>||<name>),使用 || 作为内部分隔符。 然后每个 || 分隔的段都会根据 Kubernetes 的 DNS-1123 子域规则进行验证,该规则仅允许小写字母数字字符、-.。 这比 Dapr 在其 API 边缘已经接受的字符集要严格得多,导致了 Actor 调用和提醒注册之间的不一致。 因为验证器在检查之前将每个段转换为小写,所以大写名称实际上通过了验证,而显示的错误仍然指的是仅限小写的 RFC 1123 子域。

解决方案

现在,Scheduler 使用 Dapr 在其 API 边缘应用的相同策略来验证名称,因此任何被 Actor 调用接受的内容也可以用于提醒或作业。 提醒名称、作业名称和 Actor 标识符现在可以包含除 /\\#?、控制字符(包括 NUL 字节)以及精确的路径序列 ... 之外的任何字符。 允许使用大写字母和诸如 |@ 之类的字符,|| 在 Actor ID 和名称中继续被接受。 现在,列出包含 || 的 Actor ID 的提醒也会报告正确的 Actor 元数据。 验证错误现在会描述实际被禁止的字符。

IAM Roles Anywhere X.509 认证在使用证书链、SVID 刷新和 SVID 缺失时中断

问题

AWS 组件使用的 IAM Roles Anywhere X.509 凭据提供程序无法与真实的 SPIFFE / Dapr Sentry PKI 配合工作。 它仅呈现叶子证书,因此其叶子证书由中间 CA 签名的工作负载无法根据在链根注册的信任锚点进行验证。 在短期的 SPIFFE SVID 过期后,每次凭据刷新都会因 missing required fields 失败,并且组件会失去 AWS 访问权限。 当没有 SPIFFE SVID 存在,或工作负载私钥不是 ECDSA 时,提供程序会 panic 并使整个 Sidecar 崩溃。

影响

如果你使用的 AWS 组件配置了 IAM Roles Anywhere X.509 身份验证(trustAnchorArntrustProfileArnassumeRoleArn),特别是使用包含一个或多个中间 CA 的证书链,则会受到影响。 受影响的 Sidecar 在 SVID 轮换后会失去 AWS 访问权限,而缺失或非 ECDSA 的 SVID 可能直接导致 Sidecar 崩溃。

根本原因

只有叶子证书被发送到 IAM Roles Anywhere,省略了构建到注册信任锚点路径所需的中间链。 刷新路径从构造函数从未填充的区域和 ARN 字段重建凭据提供程序,因此 SVID 过期后的任何刷新验证都会失败。 提供程序还对私钥使用了未检查的类型断言,并且没有防护缺失的 SVID,将这两种条件都变成了 panic。

解决方案

签名者现在呈现叶子证书加上中间链,通过 X-Amz-X509-Chain 头发送,以便 IAM Roles Anywhere 可以根据链根处的信任锚点进行验证。 凭据刷新现在会替换现有提供程序上的签名者,该提供程序已经保留了初始化时的区域和配置文件/锚点/角色 ARN,而不是从未设置的字段重建它。 刷新后的证书仅在签名者替换成功后存储,保持证书和签名者同步,这样刷新失败时会重试,而不是静默采用过时的签名者。 缺失的 SPIFFE SVID 和非 ECDSA 工作负载密钥现在返回错误而不是 panic,因此配置错误不再会导致 Sidecar 崩溃。

Redis 组件更新至 go-redis v9.21.0

问题

Dapr 基于 Redis 的组件使用的共享 Redis 客户端固定在 github.com/redis/go-redis/v9 v9.6.3。 升级到当前版本 v9.21.0(该版本包含多个上游版本积累的错误修复和改进)曾被构建中断阻止:v9.21.0 向 go-redis 的 XMessage 类型添加了两个字段(MillisElapsedFromDeliveryDeliveredCount),这破坏了 Dapr 用于将 XClaim 结果映射到其自身 RedisXMessage 类型的整结构体转换。

影响

如果你使用任何基于 Redis 的组件,例如 Redis 状态存储、pub/sub、配置存储或分布式锁,则会受到影响。 它们现在运行在 go-redis v9.21.0 上,而不是 v9.6.3。

根本原因

XClaimResult 使用整结构体转换(RedisXMessage(xMessage))将每个 go-redis 的 XMessage 转换为内部的 RedisXMessage,这仅在两个结构体具有相同字段时才有效。 当 v9.21.0 向 XMessage 添加字段时,该转换不再能编译。

解决方案

Redis 客户端依赖项已升级到 go-redis v9.21.0,XClaimResult 现在显式分配 IDValues 字段,与 XReadGroupResult 已使用的逐字段模式保持一致。 这使得转换能够适应上游 XMessage 类型未来的字段添加。

Kafka 组件缺少连接超时、代理健康检查以及可调的生产者配置

问题

Kafka 组件(pub/sub 和 bindings,包括 confluent 和 wurstmeister 变体)构建在一个共享的 Kafka 组件上,该组件使用 Sarama 客户端及其默认网络配置。Sarama 的 dial、read、write 和元数据超时未作为组件元数据暴露,没有方法可以探测代理可达性,并且生产者的确认和重试设置是硬编码的。对于不可达或缓慢的代理,操作——包括组件初始化——可能会阻塞比预期更长的时间,且没有健康信号来表明代理已变得不可达。

影响

如果你使用 Kafka pub/sub 或 Kafka binding 组件,则会受到影响。连接问题可能导致操作挂起,在不可达代理上初始化阻塞,没有内置检查来检测代理不可用,并且生产者的持久性和重试行为是固定的,无法调整。

根本原因

共享 Kafka 组件使用 Sarama 的默认配置创建其 Sarama 客户端,并且未将 Net dial、read、write 和元数据超时作为组件元数据暴露,因此调用者无法限制操作在网络上的等待时间。没有代理连接性探测,并且在构造同步生产者时,生产者的 RequiredAcks 和重试次数是硬编码的。

解决方案

共享 Kafka 组件现在通过组件元数据暴露可调的网络超时和生产者设置,并添加了代理健康检查:

  • 新的网络超时元数据:dialTimeoutreadTimeoutwriteTimeout(默认均为 30 秒),以及 metadataTimeout(默认为 0,表示应用 Sarama 自身的默认值)。无效值将回退到默认值。
  • 新的生产者元数据:producerRequiredAcksall(默认)、localnone)和 producerRetryMax(默认为 5),未设置时保留之前的行为。
  • 代理连接性探测,用于验证配置的代理是否可达。它禁用元数据重试,以便在集群不健康时快速失败,遵循调用者的上下文和超时,并且不修改组件状态。
  • 组件初始化不再在不可达代理上无限期阻塞。

所有默认值都保留之前的行为,因此不需要配置更改。

当创建或更新不相关的 Configuration 时,Sidecar 会重启

问题

在 Kubernetes 模式下,当其命名空间中的任何 Configuration 资源被创建或更新时,Dapr Sidecar 会执行完整的运行时重启(SIGHUP 热重载),包括 Sidecar 不使用的 Configuration 资源。Configuration 热重载会重启 Sidecar 是设计使然,但它只应针对 Sidecar 实际运行时使用的 Configuration 进行。

影响

如果你在同一个命名空间中运行多个具有不同 dapr.io/config 注解的应用,并在那里创建或更新 Configuration 资源,则会受到影响。 不相关的应用会在每次此类更改时重启其 Sidecar。 那些是其命名空间中唯一 Dapr 应用的应用,或只有单个 Configuration 的命名空间,实际上不受影响。

根本原因

Operator 通过每个应用的流将 Configuration 更新传输到 Sidecar,该流根据请求应用的命名空间和范围过滤资源。 Configuration 资源不携带范围,因此 Operator 的 informer 过滤器将该命名空间中的每个 Configuration 都视为属于每个应用,并将所有 Configuration 流式传输到每个连接的 Sidecar。 然后 Sidecar 的 SIGHUP 协调器会为它从未加载过的 Configuration 重启运行时。

解决方案

Operator 现在在服务器端确定哪个 Configuration 被分配给连接的 Sidecar(根据其 Pod 的 dapr.io/config 注解),并基于通过 mTLS 认证的应用身份进行关联,然后仅传输该 Configuration 的更新。 Sidecar 不再被信任自行报告哪个 Configuration 是它自己的,因此 Sidecar 不再能够接收不属于它的 Configuration 的更新。Sidecar 现在仅在其自身分配的 Configuration 更改时才重启。 Pod 查找使用专用的仅元数据缓存,因此 Operator 不会获取或保留完整的 Pod 对象。

Sidecar 在初始化组件之前预留其内部 gRPC 端口

问题

Dapr Sidecar(daprd)可能随机无法启动,其内部 gRPC 端口(默认 50002)报告 bind: address already in use,即使没有其他进程正在使用它。

影响

如果你的 Sidecar 加载了在初始化期间打开出站连接的组件(基于 Redis 的状态存储、pub/sub、配置存储或锁),则会受到影响。这在 Linux 上最有可能发生,并且随着组件增多和 Pod 重启更频繁而变得更常见。

根本原因

Sidecar 在绑定其内部 gRPC 端口之前初始化其组件。组件初始化会打开出站连接,操作系统可能会将内部 gRPC 端口分配为该连接的临时源端口(默认 50002 位于 Linux 临时端口范围 3276860999 内),因此 Sidecar 后续尝试在该端口上监听时会失败。

解决方案

Sidecar 现在在运行时初始化的最初阶段,在任何组件初始化之前,就绑定其内部 gRPC 端口,并将该已绑定的监听器交给内部 gRPC 服务器,而不是再次绑定该端口。 因为 Sidecar 从一开始就持有该端口,操作系统不会将其作为临时源端口分发,因此组件连接无法占用它,Sidecar 可以可靠启动。 如果在内部 gRPC 服务器启动之前初始化失败,预留的端口将被释放。 不需要配置更改。如果你之前将 dapr.io/internal-grpc-port 注解设置为动态范围之外的值(例如 61002)作为变通方法,你可以保留或移除它。

一个缓慢的 Actor 定时器回调会延迟该 Sidecar 上所有其他 Actor 的定时器

问题

Sidecar 上所有 Actor 定时器回调一次执行一个。 当一个回调正在运行时,其他定时器无法触发,因此单个缓慢的回调会延迟该 Sidecar 上托管的所有其他 Actor 的定时器,并且到期的定时器会堆积在它后面。

影响

如果你使用 Actor 定时器,并且任何定时器回调需要显著的时间返回,则会受到影响。 同一 Sidecar 上不相关 Actor 的定时器会因排在它们前面的回调的累积持续时间而延迟触发。

根本原因

Actor 定时器驻留在 Sidecar 的内存中,并由单个按时间排序的队列调度。 队列的处理循环在其自己的 goroutine 上同步调用每个到期定时器的回调,因此无论回调属于哪个 Actor,它们在整个 Sidecar 上都是串行执行的。

解决方案

到期定时器现在被路由到每个 Actor 的执行循环。 同一 Actor 的回调仍然按计划顺序一次运行一个,并且重复定时器在其当前回调返回后才会重新启动,但不同 Actor 的回调现在可以并发运行,因此一个缓慢的回调只会延迟其自身 Actor 的定时器。 每个 Actor 的循环在 Actor 的第一个定时器触发时创建,并在该 Actor 没有注册的定时器且没有未完成的触发时被回收。 删除或替换定时器现在也会取消已在同一 Actor 的运行回调后面等待的触发。

在子工作流仍处于运行状态时重用工作流实例 ID

问题

即使由先前执行启动的子工作流仍在运行,使用已完成、失败或已终止工作流的实例 ID 创建工作流也会成功。 仍在运行的子工作流属于旧执行,但会向父实例 ID 报告其完成情况,因此其事件可能会被传递到新执行中,并且一个固定了相同子 ID 的新执行可能会与旧执行的活跃子工作流发生冲突。

影响

如果你重用确定性工作流实例 ID 来创建子工作流的工作流,并重新创建了一个其子工作流尚未完成的父工作流,则会受到影响。 这发生在父工作流在不等待其子工作流的情况下完成,或在不递归的情况下被终止,从而留下子工作流运行的情况。 使用新鲜或自动生成实例 ID 创建的工作流不受影响,并且不受此修复的影响。

根本原因

创建路径仅检查了正在重新创建的实例的运行时状态。子工作流是独立的实例,其生命周期可能超过一个终止的父工作流,并且记录在先前执行历史中的子工作流从未被查询过。

解决方案

重新创建终止的工作流现在会验证先前执行的每个子工作流(递归检查并跨越应用边界)是否也处于终止状态或已被清除。 如果某个后代仍在运行或无法验证,则创建请求将被拒绝,并返回冲突错误,指出阻止的子工作流;清除工作流仍会无条件释放其实例 ID。 使用新实例 ID 的创建采取与之前相同的路径,不执行额外工作。

gRPC 流式 Actor 应用程序在不打开应用端口的情况下无法托管 Actor

问题

Dapr 1.18 引入了通过应用发起的 gRPC 回调流(SubscribeActorEventsAlpha1)托管 Actor。 此功能的一个主要特性是应用程序自己拨号 Sidecar,因此不需要监听任何端口。 实际上,针对未启动 --app-port 的 Sidecar 打开流会立即失败,出现 FailedPrecondition 错误,指出 Actor 回调流式传输需要 gRPC 应用程序通道。 应用程序被迫打开一个本地端口,并通过 --app-port(或 dapr.io/app-port 注解)将其传递给 Sidecar,纯粹是为了满足 Sidecar 的通道设置,即使 Actor 流量从未使用该端口。

影响

如果你通过流式 Actor API 托管 Actor,并且运行的 Sidecar 未配置应用端口,则会受到影响。 回调流在注册前被拒绝,因此应用程序的 Actor 类型从未注册,也无法服务任何 Actor 流量。

根本原因

只有在配置了应用端口时,Sidecar 才会创建其应用程序通道。 流端点的守卫和 Actor 传输选择都通过检查 gRPC 应用程序通道来获取回调流管理器,因此如果没有应用端口,就没有通道可供检查,流会被拒绝,就好像应用程序是基于 HTTP 的一样。

解决方案

当没有应用程序通道时,流端点现在从运行时拥有的流管理器提供回调流,并且 Actor 注册将该管理器显式传递给 Actor 传输,而不是从应用程序通道派生。 未启动应用端口的 Sidecar 现在接受回调流,注册应用程序的 Actor 类型,并通过流路由调用、提醒、定时器和停用操作,因此应用程序不需要监听任何端口。

go-chi 更新至 v5.2.4 以修复 CVE-2025-69725

问题

Dapr HTTP 服务器依赖 github.com/go-chi/chi/v5,该版本固定在 v5.2.2。该版本受 CVE-2025-69725 影响。

影响

如果你运行 daprd,则会受到影响。依赖项扫描器在构建于 go-chi v5.2.2 的 Sidecar 二进制文件上报告 CVE-2025-69725。

根本原因

github.com/go-chi/chi/v5 依赖项早于上游在 v5.2.4 中发布的修复。

解决方案

github.com/go-chi/chi/v5 更新至 v5.2.4,解决了 CVE-2025-69725。这是一个仅涉及依赖项的更改,没有行为影响。

mongo-driver 更新至 v1.17.7 以修复 CVE-2026-2303

问题

MongoDB Go 驱动 go.mongodb.org/mongo-driver 固定在 v1.14.0,该版本受 CVE-2026-2303 影响。它被 Actor 提醒子系统和 components-contrib 中的 MongoDB 组件直接使用。

影响

如果你运行 daprd,特别是如果你使用 MongoDB 状态存储,则会受到影响。依赖项扫描器在构建于 mongo-driver v1.14.0 的 Sidecar 二进制文件上报告 CVE-2026-2303。

根本原因

运行时和 components-contrib 依赖项中的 go.mongodb.org/mongo-driver 依赖项都早于上游在 v1.17.7 中发布的修复。

解决方案

go.mongodb.org/mongo-driver 更新至 v1.17.7,并且 github.com/dapr/components-contrib 依赖项已升级至 v1.18.3,以便在组件端携带相同的驱动更新。两者都解决了 CVE-2026-2303。升级保持在驱动程序的 v1 线内,因此这是一个仅涉及依赖项的更改,没有行为影响。

亚毫秒级延迟未被计入延迟指标

问题

低于一毫秒的延迟测量值从延迟直方图中被省略。

影响

耗时少于一毫秒的操作未在受影响的延迟直方图中体现,导致低延迟观测被少报。

根本原因

ElapsedSince 在将结果转换为 float64 之前除以了 time.Duration 值,将亚毫秒持续时间截断为零。现有的正延迟保护随后跳过了这些观测值。

解决方案

ElapsedSince 现在在除法之前将持续时间转换为浮点毫秒,保留了分数毫秒,并允许记录亚毫秒延迟观测值。

已终止工作流的工作流执行指标状态报告为 failed

问题

dapr_runtime_workflow_execution_count 指标未区分 failed 工作流执行和有意 terminated 的工作流执行,并将两者都报告为 failed 状态标签。

影响

运维人员期望使用此指标对工作流失败发出警报,因此 status=“failed” 应意味着工作流失败,不包括用户终止的那些。

根本原因

工作流运行时有一个简单的、两种方式的逻辑来分配指标状态:如果不是 RUNTIME_STATUS_COMPLETED,则假定为失败。

解决方案

修改此逻辑以考虑 failedterminated 状态。

当创建的子工作流实例 ID 已被使用时,父工作流陷入停滞

问题

一个创建子工作流(使用已属于另一个活跃工作流的显式实例 ID)的工作流永远不会推进。 运行时会无限期重试创建子工作流,父工作流将永远停留在 RUNNING 状态,并且不会向工作流代码报告任何错误。 Sidecar 在每次尝试时记录一条重试警告:

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'

影响

如果你的工作流创建子工作流时使用确定性的或用户提供的实例 ID,这些 ID 可能与活跃的工作流实例冲突,则会受到影响。 父工作流会无限期挂起,必须手动终止。 使用自动生成实例 ID 的子工作流不受影响。

根本原因

当调度子工作流创建时,父工作流的工作流 Actor 将每个失败都视为暂时性的,并通过其唤醒提醒进行重试。 目标实例的 AlreadyExists 拒绝不是暂时性的(只要占据的工作流仍然活跃),因此重试循环永远不会成功,并且失败从未报告给等待的子工作流任务。

解决方案

子工作流任务现在会立即失败,并返回一个命名冲突的错误(an active workflow with ID '<id>' already exists),而不是由运行时重试。 父工作流会推进:工作流代码可以处理错误并继续,将其传播以使父工作流失败,或者附加一个重试策略到子工作流调用,在这种情况下,创建会被重新尝试,并在实例 ID 变为可用时成功。 当目标 ID 属于一个终止工作流(其子工作流树尚未终止)时,也适用相同的处理,同样适用于跨应用子工作流和由工作流重新运行驱动的子工作流。 对已占用实例 ID 的分离工作流生成会被丢弃并记录警告,与其即发即忘的语义一致。 占据实例 ID 的工作流永远不会因被拒绝的创建而受到影响。

更新内容 (原始)

Dapr 1.18.2

This update contains the following bug fixes:

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, and writeTimeout (each default 30s), and metadataTimeout (default 0, meaning Sarama’s own default applies). Invalid values fall back to the defaults.
  • New producer metadata: producerRequiredAcks (all (default), local, or none) and producerRetryMax (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 3276860999), 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.

下载链接