https://github.com/shangxiang0907/codex-wsl-clipboard-bridge
https://github.com/shangxiang0907/codex-wsl-clipboard-bridge/commit/fabfb4af94ef1e3cb3e8901e49344785742a8536
文章目录
- 一张彩色截图为什么让 Codex 误报“剪贴板没有图片”
- 现象
- 先把链路拆开
- 几次合理但错误的猜测
- 会不会是会话上下文太长
- 会不会是 Wayland 和 X11 后端选错
- 会不会是颜色太丰富,PNG 压缩算法有问题
- 抓住 X11 selection 的所有权变化
- 真正的根因:分片之间只等 10 毫秒
- 修复:绕开 INCR,而不是牺牲图片
- 验证
- 对系统其他服务的影响
- 排障方法上的收获
一张彩色截图为什么让 Codex 误报“剪贴板没有图片”
从 WSLg、X11 selection、
xclip到 Codexarboard的 10 毫秒超时
现象
在 WSLg 中运行 Codex CLI 时,同样使用Ctrl+V:
- 一些界面截图可以正常粘贴;
- 一张
2520×1490的游戏指南页面截图却反复失败; - Codex 报错称剪贴板为空,或没有它请求的图片格式:
Failed to paste image: no image on clipboard: The clipboard contents were not available in the requested format or the clipboard is empty.这条错误很容易让人怀疑图片分辨率、颜色数量、PNG 大小,甚至会话上下文长度。
实际根因都不在这些地方。
先把链路拆开
这个环境中的完整数据流是:
Windows 截图 → WSLg Wayland image/bmp → ImageMagick PNG32 → X11 CLIPBOARD image/png → Codex CLI / arboard服务日志已经确认 BMP 读取、PNG 转换和 X11 发布成功:
bridged 2520x1490 image (11264454 byte BMP) to X11 as PNG在发生故障的同一个终端中,xclip也能完整读出图片:
xclip-selectionclipboard-tTARGETS-o|tr'\0''\n'xclip-selectionclipboard-timage/png-o>/tmp/clipboard-test.pngfile/tmp/clipboard-test.pngstat-c'%s bytes'/tmp/clipboard-test.png结果是有效的2520×1490RGBA PNG,大小约 1.37 MB。这排除了格式缺失、
文件损坏和超大输入限制。
几次合理但错误的猜测
会不会是会话上下文太长
不会。错误发生在图片进入对话之前,是本地剪贴板读取失败。conversation recap
和模型上下文还没有机会处理这张图片。
会不会是 Wayland 和 X11 后端选错
强制删除WAYLAND_DISPLAY并让 Codex 使用DISPLAY=:0后仍然失败。
这说明后端选择不是这次问题的充分解释。
会不会是颜色太丰富,PNG 压缩算法有问题
同尺寸合成图测试确实显示,内容熵会显著改变 PNG 大小:
| 内容 | BMP | PNG32 |
|---|---|---|
| 纯色 | 11.26 MB | 17.9 KB |
| 渐变 | 11.26 MB | 18.8 KB |
| 照片式纹理 | 11.26 MB | 7.58 MB |
| 随机颜色 | 11.26 MB | 12.92 MB |
但实际失败截图只有约 1.37 MB,而且 ImageMagick、file、identify和xclip都能正常处理。颜色丰富度只是增加故障概率的相关因素,不是根因。
我们一度实现了自适应 PNG8 量化,实测没有解决问题,随后完整撤销。这一点很
重要:没有端到端证据时,不应以损失画质来掩盖传输层缺陷。
抓住 X11 selection 的所有权变化
X11 剪贴板不是一块永久保存数据的共享内存。当前 selection owner 必须在消费方
请求格式时现场提供数据。
通过 XFixes 监听复现过程,得到如下事件:
19:30:00 event=changed TIMESTAMP,TARGETS,UTF8_STRING,TEXT 19:30:02 event=changed TARGETS,image/png xclip -selection clipboard -t image/png -i candidate.png第二个事件之后没有其他程序覆盖 selection。Codex 失败时,PNG owner 仍然存在,
并且image/png仍在TARGETS中。
问题因此被压缩到最后一段:Codex 如何从 X11 owner 读取 PNG。
真正的根因:分片之间只等 10 毫秒
Codex CLI 0.152.0 包含arboard 3.6.1。检查该版本的src/platform/linux/x11.rs
可以看到:
constLONG_TIMEOUT_DUR:Duration=Duration::from_millis(4000);constSHORT_TIMEOUT_DUR:Duration=Duration::from_millis(10);读取开始时,arboard最多等待 4 秒。如果 selection owner 使用 X11INCR
协议分片发送大属性,每收到一个有效分片后,下一分片的等待时间却被重置为仅
10 毫秒:
*timeout_end=Instant::now()+SHORT_TIMEOUT_DUR;WSLg、X server 或低优先级xclip只要有一次调度间隔超过 10 毫秒,arboard就返回ContentNotAvailable。Codex 将它格式化成“no image on
clipboard”,掩盖了真实的分片超时。
这也解释了为什么故障看似与颜色和大小有关:PNG 越大,INCR 分片越多,撞上一次
10 毫秒调度空隙的概率越高。但它不是稳定的字节阈值,同一张图也可能偶发成功。
在 X11 协议(X Window System)的上下文中,INCR 是 Incremental Transfer(增量传输)机制的缩写。
它是 X11 剪贴板(Selection)协议中用于处理大数据量传输的一种握手协议。
修复:绕开 INCR,而不是牺牲图片
从消费者侧看,理想修复是让arboard使用合理的分片超时,例如每个有效分片后
重新等待 1~4 秒。不过桥接器不能修改用户当前安装的 Codex。
本项目采用生产者侧兼容修复:用一个小型 Python/ctypesX11 PNG selection
owner 替代xclip。
新 owner:
- 只操作当前用户的 X11
CLIPBOARD; - 只声明
TARGETS、image/png和TIMESTAMP; - 调用
XExtendedMaxRequestSize启用 X11 BIG-REQUESTS; - 在安全上限内用一次
XChangeProperty返回完整 PNG; - 不使用 INCR,因此不会触发
arboard的 10 毫秒分片超时; - selection 被其他程序接管时收到
SelectionClear并退出; - 服务停止时由 systemd control group 一并清理。
本机 X server 报告的最大请求大小约为 16.7 MB。实现额外预留了请求头和 Xlib
封装空间;超过安全上限时明确失败,不会截断或静默损坏图片。
验证
先用一张约 7.58 MB 的高复杂度 PNG 做逐字节往返测试:
direct owner round-trip: 7578809 bytes随后执行仓库验证:
XFixes event delivery: OK Static and runtime verification: OK最后重新截取同一个2520×1490Dungeon Lootr Beginner Guide 页面,Codex
立即成功粘贴并能正确识别页面导航、状态卡、地下城主图和页面目录。
对系统其他服务的影响
新 owner 的权限和作用范围与原来的xclipowner 基本一致:
- 不监听网络;
- 不创建系统级服务;
- 不操作 X11
PRIMARY或SECONDARYselection; - 不读取图片以外的剪贴板内容;
- 新的复制操作会自然取代它并使其退出;
- PNG 只在用户私有运行目录和 owner 进程内存中短暂存在。
它改变的是图片数据的 X11 传输方式,而不是桌面的剪贴板语义。
排障方法上的收获
这次问题最值得保留的不是某一条 ImageMagick 参数,而是分层验证的方法:
源格式是否存在 → 转换结果是否有效 → selection owner 是否仍存在 → TARGETS 是否包含消费方请求的 MIME → 直接读取是否成功 → 消费方具体依赖如何处理协议“没有图片”只是上层错误文案,不代表剪贴板真的为空。只有把 Wayland、X11 selection、MIME 转换、INCR/BIG-REQUESTS 和消费库逐层拆开,才能找到真正需要修改的那一行。