fix(agent): ETIMEDOUT 给出「配代理」提示 —— 境内直连最高频失败形态无指引
真机(Windows 192.168.0.160)验证自更新出网时发现: `connect ETIMEDOUT 20.205.243.166:443` 落到 describeNetError 的兜底分支, 只回一串 IP:端口超时 —— 而这正是境内直连 GitHub 最常见的形态,用户唯一 有效动作就是配代理,却完全没被告知。 修法:新增 ETIMEDOUT / EHOSTUNREACH / ENETUNREACH 分支,点明 「设置 NA_PROXY=<代理地址> 或配置系统代理后重试」。与 ECONNRESET 的 「代理或线路问题」保持区分(两类原因不同,单测钉死互不串味)。 真机复验:重新部署后同一命令已输出新提示。
This commit is contained in:
1 parent
0b858bb343
commit
c062d1cc8b
2 files changed
+26
No files matched your search
@@ -102,6 +102,12 @@ export function describeNetError(err: unknown, timeoutMs: number): string {
|
||||
if (code === 'ENOTFOUND' || code === 'EAI_AGAIN' || code === 'ECONNREFUSED') {
|
||||
return `${msg}(域名解析失败或目标拒绝连接;若配了代理,请核对代理地址与端口)`;
|
||||
}
|
||||
// 2026-10-27 真机补:境内直连 GitHub 的**最常见**失败形态就是 TCP 层 ETIMEDOUT
|
||||
// (不是我们自己的 timeoutMs,也不是 ECONNRESET)。原先它落到下面兜底分支,
|
||||
// 用户只看到一串 IP:443 超时,不知道该干什么 —— 而这时唯一有效的动作就是配代理。
|
||||
if (code === 'ETIMEDOUT' || code === 'EHOSTUNREACH' || code === 'ENETUNREACH') {
|
||||
return `${msg}(连接超时/网络不可达;若目标在境外或需要代理,请设置 NA_PROXY=<代理地址> 或配置系统代理后重试)`;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,3 +85,23 @@ test('错误分类:DNS/拒连单独一类,普通错误原样透出', () => {
|
||||
const plain = new Error('boom');
|
||||
assert.equal(describeNetError(plain, 1000), 'boom', '无法归类的错误原样透出,不编造原因');
|
||||
});
|
||||
|
||||
test('describeNetError:TCP 层 ETIMEDOUT 必须给出「配代理」的可执行提示(2026-10-27 真机)', () => {
|
||||
// 真机现场:境内 Windows 直连 github.com:443 → connect ETIMEDOUT 20.205.243.166:443。
|
||||
// 这条最常见,原先落到兜底分支只回一串超时,用户不知道唯一有效动作是配代理。
|
||||
const etimedout = Object.assign(new Error('connect ETIMEDOUT 20.205.243.166:443'), { code: 'ETIMEDOUT' });
|
||||
const msg = describeNetError(etimedout, 20_000);
|
||||
assert.match(msg, /连接超时\/网络不可达/);
|
||||
assert.match(msg, /NA_PROXY/, '必须点明 NA_PROXY 这个具体开关');
|
||||
assert.match(msg, /系统代理/);
|
||||
|
||||
// 同族错误走同一提示
|
||||
for (const code of ['EHOSTUNREACH', 'ENETUNREACH']) {
|
||||
assert.match(describeNetError(Object.assign(new Error('x'), { code }), 1000), /NA_PROXY/, code);
|
||||
}
|
||||
|
||||
// 不得与 ECONNRESET 的「代理或线路问题」混为一谈(两类原因不同)
|
||||
const reset = Object.assign(new Error('read ECONNRESET'), { code: 'ECONNRESET' });
|
||||
assert.match(describeNetError(reset, 1000), /代理或线路/);
|
||||
assert.doesNotMatch(describeNetError(reset, 1000), /NA_PROXY/);
|
||||
});
|
||||
Reference in new issue
Block a user