Skip to content

fix(gamesign): 通知返回值改为 DispatchResult 后未同步两处调用方,签到通知必抛 TypeError #514

Description

@qiyinxi

由 Sentry 每日分诊自动建立。Sentry issue: AUTO-MAS-BACKEND-3P
https://auto-mas.sentry.io/issues/AUTO-MAS-BACKEND-3P

现象

TypeError: can only join an iterable,11 条事件,release auto-mas@v5.5.0-beta.2(dist 8b071e63e186),用户分布 CN。首次出现 2026-08-31T09:39Z,与 #417 合并时间吻合。

File "app\api\tools.py", line 66, in _track_game_sign_notification
  logger.warning(f"后台游戏签到通知部分失败: {'、'.join(failed_channels)}")
TypeError: can only join an iterable

定位

当前 dev 上有两处同源缺陷,Sentry 只抓到了第一处:

  • app/api/tools.py:66_track_game_sign_notification(),后台通知回调路径(已上报)
  • app/api/tools.py:191game_sign() 内对 _dispatch_game_sign_notification() 返回值的处理(未上报,见下)

根因

8b071e63#417 通知系统统一迁移,2026-08-31)把 app/tools/game_sign_notify.py

async def push_game_sign_notification(results: list[dict]) -> list[str]:

改成了

async def push_game_sign_notification(results: list[dict]) -> DispatchResult:

app/api/tools.py 的两个消费方仍按 list[str] 使用返回值:

  1. _track_game_sign_notificationfailed_channels = task.result() 现在拿到的是 DispatchResult
  2. _dispatch_game_sign_notification 的签名仍写着 -> list[str] | None,实际把 DispatchResult 透传给了 game_sign()

DispatchResult@dataclass(frozen=True),既不可迭代、也没有 __bool__,因此:

  • if failed_channels: 恒为真(即使 attempted=0failed=() 的空结果);
  • 随后的 '、'.join(...) 必抛 TypeError

失败渠道名实际在 DispatchResult.failedtuple[str, ...]),两处应改为取 .failed,并把 _dispatch_game_sign_notification 的返回标注改成 DispatchResult | None

复现与影响

开启签到通知(ToolsConfig.GameSign.NotifyEnabled)后执行游戏签到即可触发,两条路径按通知耗时分流:

  • 通知在 100ms 内完成 → 走 tools.py:191。异常被 game_sign()except Exception 吞掉,接口返回 500 TypeError: can only join an iterable,用户看到签到失败,但签到其实已成功并落盘。
  • 通知超过 100ms 转后台 → 走 tools.py:66。异常落在 asyncio 回调里,即 Sentry 上报的这条;此路径不影响接口返回,只丢日志。

前一条路径未在 Sentry 单独成组(被 game_sign() 的 500 分支吸收),所以实际影响面大于这 11 条事件。

Metadata

Metadata

Assignees

No one assigned

    Labels

    Sentry dailySentry daily issuesbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions