Bun Unix socket 报 EINVAL:先查长路径的父目录
Bun 1.4.4 在长 Unix socket 路径且父目录不存在时,可能把底层目录错误表现为 EINVAL。核对路径字节长度与父目录,避免把所有 EINVAL 当成守护进程未启动。
连接本地守护进程时,代码原本在 ENOENT 后启动服务,换到 Bun 后却提前收到 EINVAL,自动启动逻辑没有执行。如果 Unix socket 路径很长、父目录又尚未创建,应先核对路径和目录状态。
Bun 1.4.4 在 macOS、Linux 存在这种错误码差异。它不表示所有 EINVAL 都是文件不存在,也不意味着捕获 EINVAL 后直接启动守护进程就安全。
长路径会进入另一条连接路径
Unix socket 的文件系统路径受操作系统的 sockaddr_un.sun_path 长度限制。长度按字节计算,包含中文的路径不能只看 JavaScript 的字符串字符数;macOS 与 Linux 的边界也不同。
Bun 的相关实现会在长路径场景先打开父目录,再尝试连接。1.4.4 这段实现中,打开父目录失败后会覆盖原来的 errno,因此父目录不存在这类错误可能最终表现为 EINVAL,而不是调用方期待的 ENOENT。
已有具体表现包括 macOS 的 104、108 字节路径,以及 Linux 的 108 字节路径。更长的路径也可能是两种运行时都不能接受的真实无效参数,不能把这些数值写成通用可用上限,更不能仅靠错误码证明命中了该路径。
将端点放到短而受控的目录
先打印程序实际使用的绝对 socket 路径及其字节数,并核对父目录是否存在、是否是目录、当前用户是否有访问权限。可以在已有诊断代码中使用:
import path from 'node:path';
import { stat } from 'node:fs/promises';
console.log(Buffer.byteLength(socketPath));
const parent = await stat(path.dirname(socketPath));
console.log(parent.isDirectory());
这里的 socketPath 应是程序已有的绝对文件系统路径。stat() 的异常要保留具体错误,不要捕获后一律解释成“目录不存在”。这段检查只观察父目录,不能证明服务正在监听。
如果原端点由很深的工作区路径拼接而成,可在现有运行目录约定中选用更短的专用目录,并让客户端与服务端共同更新地址。目录仍应属于预期用户,权限按应用需要限制;不要为缩短路径把可写 socket 任意放进所有用户都能替换的共享位置。
父目录确实属于当前应用、按设计应由它创建时,再通过正常初始化流程创建。不要因为某次 EINVAL 就递归创建任意输入路径,也不要删除不明来源的现存 socket 文件。
启动守护进程前再确认一次状态
客户端的“连接失败后启动”需要区分服务不存在、权限不足、路径无效与服务正在启动。把 EINVAL 并入 ENOENT 分支,会掩盖真实配置错误,还可能并发启动多个服务。
调整端点后,分别验证首次启动时目录尚不存在、服务已经运行、服务正常退出后再次启动,以及目录权限错误四条路径。连接成功后还要完成一次正常协议交互;只看 socket 文件出现,不能证明它属于当前服务。
这里限定 Bun 1.4.4 的文件系统 Unix socket 路径;Windows 命名管道和 Linux 抽象 socket 有各自的命名规则。其他正式版本仍需按实际路径和父目录状态验证,不能把某个固定字节数当作必然失败的阈值。


