背景
在 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-cursor0 或 libxcb-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.target,systemctl --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。