背景

在 Arch Linux + KDE Plasma 桌面环境中,OpenClaw Gateway 作为用户级 systemd 服务运行。为了避免在配置文件中保存明文密钥,Gateway 的认证信息通过 KDE Wallet 读取。这个设计本身是合理的:密钥交给桌面会话的钱包管理,服务启动时再通过本地 secret provider 取出。

问题出现在一次系统或应用更新之后。原本已经调整过的用户级 systemd 启动关系,被重新生成了一个 default.target.wants 下的链接,导致 OpenClaw Gateway 再次过早启动。由于启动时间早于 KDE 图形会话完全就绪,也早于 KWallet 通过 PAM 完成解锁,Gateway 无法读取钱包中的 secret,最终启动失败。

这个问题表面上像是 KWallet 崩溃、Qt 插件缺失,甚至 KDE 环境异常;但实际根因是:用户级 systemd 服务被错误地挂回了 default.target,启动顺序早于图形会话和钱包解锁。

现象

系统启动后,OpenClaw Gateway 没有稳定进入可用状态。日志中可以看到类似信息:

Gateway failed to start: required secrets are unavailable
SecretProviderResolutionError: Exec provider "kwallet_gateway" exited with code 1

同时,KWallet 相关日志中还可能出现:

Couldn't start kwalletd
Could not activate remote peer 'org.kde.kwalletd6'
Could not load the Qt platform plugin "xcb"

以及一批由崩溃报告处理器产生的失败项:

drkonqi-coredump-processor@...

这些信息很容易把排查方向带偏。第一眼看上去,似乎是 KDE Wallet 本身损坏,或者 Qt 的 xcb 插件依赖缺失。进一步检查后发现,KDE Plasma 主体并没有问题:核心桌面进程正常运行,用户级 KDE 服务也没有显示出会导致桌面不可用的异常。

真正异常的地方,是 OpenClaw Gateway 在 KWallet 可用之前就开始读取 secret。

一个容易误判的线索:xcb-cursor0

日志里出现了类似提示:

From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin.

这条日志很有迷惑性。它看起来像是在提示缺少 xcb-cursor0libxcb-cursor0 包。但在 Arch Linux 上,对应包名不是 xcb-cursor0,而是:

xcb-util-cursor

如果执行:

pacman -Q xcb-util-cursor
pacman -Ql xcb-util-cursor | grep -E 'libxcb-cursor|xcb/cursor'

并确认系统已经存在:

/usr/lib/libxcb-cursor.so
/usr/lib/libxcb-cursor.so.0
/usr/lib/libxcb-cursor.so.0.0.0

那就不能继续按“缺包”方向处理。

这次问题的关键并不是缺少库,而是服务启动时机不对。

核心排查:用户级 systemd 的启动目标

检查 OpenClaw Gateway 的用户级 service:

systemctl --user cat openclaw-gateway.service

可以看到服务文件中存在类似内容:

[Install]
WantedBy=default.target

同时又有 drop-in 配置试图把它放到图形会话之后:

[Unit]
After=graphical-session.target plasma-kwallet-pam.service
PartOf=graphical-session.target

表面上看,After=graphical-session.target 已经要求服务在图形会话之后启动。但 systemd 的关键点在于:

After= 只定义顺序,不负责拉起目标;
WantedBy= 才决定 enable 后服务会挂到哪个 target 下面。

如果服务仍然被 default.target 拉起,而 graphical-session.target 并没有在同一个启动事务中被拉起,那么 After=graphical-session.target 并不能保证 OpenClaw Gateway 一定等到 KDE 图形会话和 KWallet 都准备好。

进一步检查 symlink:

find ~/.config/systemd/user -type l -name '*openclaw-gateway*' -ls

可以看到同时存在类似两个链接:

~/.config/systemd/user/default.target.wants/openclaw-gateway.service
~/.config/systemd/user/graphical-session.target.wants/openclaw-gateway.service

这就是问题所在。

虽然之前已经手动删除过 default.target.wants 下的链接,但在系统更新、应用升级或服务重新 enable 之后,由于 unit 文件里的 [Install] 仍然写着 WantedBy=default.targetsystemctl --user enable openclaw-gateway.service 会再次生成这个链接。

于是问题复发:OpenClaw Gateway 又被 default target 过早拉起。

正确修复思路

修复目标不是重装组件,也不是清理一堆表面上的 coredump,而是让 OpenClaw Gateway 只在图形会话目标下启动。

1. 删除错误的 default.target 链接

rm -v ~/.config/systemd/user/default.target.wants/openclaw-gateway.service
systemctl --user daemon-reload

2. 确认只剩 graphical-session.target 链接

find ~/.config/systemd/user -type l -name '*openclaw-gateway*' -ls

理想状态是只剩:

~/.config/systemd/user/graphical-session.target.wants/openclaw-gateway.service

3. 修改 unit 的 Install 目标

为了防止以后重新 enable 时又生成 default 链接,应该把用户级 service 文件中的:

[Install]
WantedBy=default.target

改为:

[Install]
WantedBy=graphical-session.target

修改前建议先备份:

UNIT="$HOME/.config/systemd/user/openclaw-gateway.service"

cp -av "$UNIT" "$UNIT.$(date +%Y%m%d-%H%M%S)"

perl -0pi -e 's/\[Install\]\nWantedBy=default\.target/[Install]\nWantedBy=graphical-session.target/' "$UNIT"

然后重新加载并重建 enable 链接:

systemctl --user daemon-reload
systemctl --user disable openclaw-gateway.service
systemctl --user enable openclaw-gateway.service

再检查:

find ~/.config/systemd/user -type l -name '*openclaw-gateway*' -ls

4. 验证 default.target 不再拉起 OpenClaw

systemctl --user list-dependencies default.target | grep openclaw || echo "default.target OK: no openclaw"

确认图形会话目标包含 OpenClaw:

systemctl --user list-dependencies graphical-session.target | grep openclaw || echo "WARNING: graphical-session.target does not show openclaw"

5. 重启服务并查看日志

systemctl --user restart openclaw-gateway.service
systemctl --user status openclaw-gateway.service --no-pager

查看 Gateway 日志:

journalctl --user -u openclaw-gateway.service -b --no-pager | tail -100

查看 KWallet 与 secret provider 相关错误:

journalctl --user -b --no-pager \
  | grep -Ei 'kwallet|kwalletd6|kwallet_gateway|SECRETS_RELOADER|Gateway failed|xcb' \
  | tail -120

如果日志中不再出现:

required secrets are unavailable
kwallet_gateway exited with code 1
Gateway failed to start

就说明服务启动顺序已经恢复正常。

经验总结

这次问题的本质,是 systemd 用户级服务的 enable 目标和桌面会话生命周期不匹配。

对于依赖 KDE、KWallet、D-Bus、图形会话、Portal 或桌面环境变量的用户服务,不应该简单挂到:

WantedBy=default.target

更合适的目标通常是:

WantedBy=graphical-session.target

并配合:

After=graphical-session.target
PartOf=graphical-session.target

如果服务还依赖 KWallet 自动解锁,则还需要特别注意 PAM 解锁时机。After=plasma-kwallet-pam.service 可以提供一定帮助,但不能替代正确的 WantedBy。因为 After= 只描述顺序,不决定服务由哪个 target 拉起。

此外,应用更新或重装脚本可能会重新执行:

systemctl --user enable openclaw-gateway.service

如果 unit 文件里的 [Install] 仍然指向 default.target,那么之前手动删除的 symlink 就可能被重新创建。这个问题不会每次都立刻暴露,但一旦服务依赖 KWallet、图形会话或 D-Bus,就很容易在下一次登录时变成偶发启动失败。

更新后的检查清单

每次 OpenClaw 或系统环境更新后,可以用以下命令快速确认是否复发:

find ~/.config/systemd/user -type l -name '*openclaw-gateway*' -ls

如果看到:

default.target.wants/openclaw-gateway.service

就说明服务又被挂回了过早启动的位置,需要删除 default 链接并重新确认 [Install] 目标。

同时检查:

systemctl --user list-dependencies default.target | grep openclaw || echo "default.target OK: no openclaw"
systemctl --user list-dependencies graphical-session.target | grep openclaw || echo "WARNING: graphical-session.target does not show openclaw"

最后确认服务状态:

systemctl --user status openclaw-gateway.service --no-pager
journalctl --user -u openclaw-gateway.service -b --no-pager | tail -100

结论

这类问题最容易被误判为“桌面环境坏了”“KWallet 损坏”“缺少 Qt 插件”或“系统更新造成 KDE 异常”。但从排查结果看,KDE Plasma 主体运行正常,真正的问题是用户级 systemd 服务被错误地挂到了 default.target,导致 OpenClaw Gateway 在 KDE 图形会话和 KWallet 解锁完成之前启动。

修复的关键不是重装组件,而是把服务生命周期放回正确的位置:让 OpenClaw Gateway 跟随 graphical-session.target,而不是跟随过早的 default.target

这个案例也说明,桌面 Linux 中的用户级后台服务,只要依赖图形会话、D-Bus、KWallet 或桌面环境变量,就不能只看服务是否 enabled,还必须看它到底 enabled 到了哪个 target。

Leave a Reply

Your email address will not be published. Required fields are marked *