fix(agent): keep the error code when a request fails without a message

Node 22 rejects a refused connection to a host that resolves to both ::1
and 127.0.0.1 with an AggregateError whose message is empty and whose code
is ECONNREFUSED. The readiness fallback dropped that code, and the agent
CLI's top-level error line and the MCP server's "Cannot reach Codeman"
error printed nothing at all for it.

Add describeRequestError() (message, else code, else a generic line) and
use it at all three sites.
This commit is contained in:
Aamer Akhter committed 2026-10-24 22:39:58 -04:00
1 parent d25d71b595
commit 77ce4816a8
4 files changed
+54 -4

No files matched your search

+14 -2
View File
@@ -343,6 +343,18 @@ export const httpRequest: ApiRequest = (ctx, options) => {
});
};
/**
* One line describing a thrown request error. Node 22 rejects a refused connection to a host
* that resolves to both ::1 and 127.0.0.1 with an AggregateError whose `message` is empty, so
* fall back to its `code` (ECONNREFUSED) before a generic line rather than printing nothing.
*/
export function describeRequestError(error: unknown): string {
const message = getErrorMessage(error);
if (message) return message;
const code = (error as { code?: unknown } | null | undefined)?.code;
return code ? String(code) : 'request failed without a message';
}
/** One line describing a failed response, for humans. Plain-text guards (401/403/429) have no envelope. */
export function describeFailure(res: ApiResponse): string {
if (res.json && !res.json.success) {
@@ -573,7 +585,7 @@ export async function agentSpawn(deps: AgentDeps, options: SpawnOptions): Promis
ready = !dead && Boolean(result?.matched);
}
} catch (error) {
readinessError = getErrorMessage(error) || 'Readiness request failed';
readinessError = describeRequestError(error);
}
}
@@ -1042,7 +1054,7 @@ async function run(json: boolean, verb: (deps: AgentDeps) => Promise<number>): P
try {
process.exitCode = await verb(deps);
} catch (err) {
console.error(palette.err(`${GLYPH.fail} ${getErrorMessage(err)}`));
console.error(palette.err(`${GLYPH.fail} ${describeRequestError(err)}`));
process.exitCode = EXIT.error;
}
}
+2 -1
View File
@@ -38,6 +38,7 @@ import {
buildSendBody,
composerReadyMark,
describeFailure,
describeRequestError,
httpRequest,
inputRefusal,
isSelfSession,
@@ -497,7 +498,7 @@ export function createToolRunner(deps: ToolRunnerDeps) {
} catch (err) {
if (signal?.aborted) throw err;
throw new ToolError(
`Cannot reach Codeman at ${ctx.apiUrl}: ${getErrorMessage(err)}. Check that it is running. If every call ` +
`Cannot reach Codeman at ${ctx.apiUrl}: ${describeRequestError(err)}. Check that it is running. If every call ` +
'fails at once, a sandbox around this MCP server may be blocking network access to this machine.'
);
}