Skip to content

Commit a03ee0c

Browse files
committed
fix(agent): timeout error
1 parent 05860b3 commit a03ee0c

2 files changed

Lines changed: 48 additions & 8 deletions

File tree

packages/commands/src/commands/managed-agent/_engine/errors.ts

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,47 @@ function parseSdkResponseBody(raw: string): ApiErrorBody {
3333
return { message: raw.trim() || undefined };
3434
}
3535

36+
/**
37+
* The SDK's session polling deadline surfaces as a plain `UserError` (no
38+
* dedicated timeout class as of SDK 0.3.x), so it is recognized by its stable
39+
* message shape: "Session did not complete within the timeout (N seconds)."
40+
* (session-runtime's assertNotTimedOut — the SDK's only timeout UserError).
41+
* It is a client-side wait limit, not a usage mistake → per bl's error
42+
* boundary it must exit TIMEOUT, not USAGE.
43+
*/
44+
function isSdkPollingTimeout(error: UserError): boolean {
45+
return /did not complete within the timeout/i.test(error.message);
46+
}
47+
3648
/**
3749
* Run an SDK-backed operation, translating SDK error types into BailianError so
3850
* bl's error handler produces the right exit code and hint formatting.
39-
* SDK `UserError` → USAGE; SDK `ApiError` (server HTTP error) → GENERAL via
40-
* `mapApiError` (server message passed through verbatim, with
41-
* httpStatus/apiCode/requestId metadata for --output json); fetch transport
42-
* failures (`TypeError: fetch failed`) are rethrown untouched so the runtime
43-
* error handler maps them to NETWORK with an errno-specific hint, matching the
44-
* native client path; any other Error → GENERAL (message passed through, per
45-
* bl's "don't translate server errors" boundary).
51+
* SDK `UserError` → USAGE — except the polling-deadline UserError, which is a
52+
* client-side timeout → TIMEOUT with a wait-longer hint; SDK `ApiError`
53+
* (server HTTP error) → GENERAL via `mapApiError` (server message passed
54+
* through verbatim, with httpStatus/apiCode/requestId metadata for
55+
* --output json); fetch transport failures (`TypeError: fetch failed`) are
56+
* rethrown untouched so the runtime error handler maps them to NETWORK with an
57+
* errno-specific hint, matching the native client path; any other Error →
58+
* GENERAL (message passed through, per bl's "don't translate server errors"
59+
* boundary).
4660
*/
4761
export async function withAgentErrors<T>(fn: () => Promise<T>): Promise<T> {
4862
try {
4963
return await fn();
5064
} catch (error) {
5165
if (error instanceof BailianError) throw error;
52-
if (error instanceof UserError) throw new BailianError(error.message, ExitCode.USAGE);
66+
if (error instanceof UserError) {
67+
if (isSdkPollingTimeout(error)) {
68+
throw new BailianError(
69+
error.message,
70+
ExitCode.TIMEOUT,
71+
// `bl` prefix is safe: agent commands ship on `bl` only.
72+
"The session may still be running — check `bl managed-agent session get --session-id <id>` or `session events`.",
73+
);
74+
}
75+
throw new BailianError(error.message, ExitCode.USAGE);
76+
}
5377
if (error instanceof Error && isSdkApiError(error)) {
5478
throw mapApiError(error.statusCode, parseSdkResponseBody(error.responseBody));
5579
}

packages/commands/tests/managed-agent-errors.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,22 @@ test("SDK UserError maps to USAGE", async () => {
3939
expect(mapped.message).toBe("bad agents.yaml");
4040
});
4141

42+
test("SDK polling-timeout UserError maps to TIMEOUT (5), not USAGE", async () => {
43+
// 消息形状来自 SDK session-runtime 的 assertNotTimedOut —— 客户端等待超时,
44+
// 按 bl 错误边界必须归 TIMEOUT,不能告诉自动化调用方“参数错误”。
45+
const mapped = await catchMapped(
46+
new UserError("Session did not complete within the timeout (600 seconds)."),
47+
);
48+
expect(mapped.exitCode).toBe(ExitCode.TIMEOUT);
49+
expect(mapped.message).toBe("Session did not complete within the timeout (600 seconds).");
50+
expect(mapped.hint).toMatch(/session get/);
51+
});
52+
53+
test("提及 timeout 但非轮询超时句式的 UserError 仍归 USAGE", async () => {
54+
const mapped = await catchMapped(new UserError("Invalid timeout value in agents.yaml"));
55+
expect(mapped.exitCode).toBe(ExitCode.USAGE);
56+
});
57+
4258
test("SDK ApiError with DashScope-style JSON body surfaces clean message and api metadata", async () => {
4359
const body = JSON.stringify({
4460
code: "InvalidParameter",

0 commit comments

Comments
 (0)