























@@ -127,6 +127,27 @@ function appendBrowserToolModelHint(message: string): string {
127127return `${message} ${BROWSER_TOOL_MODEL_HINT}`;
128128}
129129130+type BrowserFetchFailureKind = "timeout" | "aborted" | "persistent";
131+132+function classifyBrowserFetchFailure(err: unknown): BrowserFetchFailureKind {
133+const msg = normalizeErrorMessage(err);
134+const msgLower = normalizeLowercaseStringOrEmpty(msg);
135+const nameLower = err instanceof Error ? normalizeLowercaseStringOrEmpty(err.name) : "";
136+const looksLikeTimeout =
137+nameLower.includes("timeout") || msgLower.includes("timed out") || msgLower.includes("timeout");
138+if (looksLikeTimeout) {
139+return "timeout";
140+}
141+const looksLikeAbort =
142+nameLower === "aborterror" ||
143+msgLower.includes("aborterror") ||
144+msgLower.includes("aborted") ||
145+msgLower.includes("abort") ||
146+msgLower.includes("cancelled") ||
147+msgLower.includes("canceled");
148+return looksLikeAbort ? "aborted" : "persistent";
149+}
150+130151async function discardResponseBody(res: Response): Promise<void> {
131152try {
132153await res.body?.cancel();
@@ -137,32 +158,36 @@ async function discardResponseBody(res: Response): Promise<void> {
137158138159function enhanceDispatcherPathError(url: string, err: unknown): Error {
139160const msg = normalizeErrorMessage(err);
140-const suffix = `${resolveBrowserFetchOperatorHint(url)} ${BROWSER_TOOL_MODEL_HINT}`;
161+const kind = classifyBrowserFetchFailure(err);
162+const suffix =
163+kind === "persistent"
164+ ? `${resolveBrowserFetchOperatorHint(url)} ${BROWSER_TOOL_MODEL_HINT}`
165+ : resolveBrowserFetchOperatorHint(url);
141166const normalized = msg.endsWith(".") ? msg : `${msg}.`;
142167return new Error(`${normalized} ${suffix}`, err instanceof Error ? { cause: err } : undefined);
143168}
144169145170function enhanceBrowserFetchError(url: string, err: unknown, timeoutMs: number): Error {
146171const operatorHint = resolveBrowserFetchOperatorHint(url);
147-const msg = String(err);
148-const msgLower = normalizeLowercaseStringOrEmpty(msg);
149-const looksLikeTimeout =
150-msgLower.includes("timed out") ||
151-msgLower.includes("timeout") ||
152-msgLower.includes("aborted") ||
153-msgLower.includes("abort") ||
154- msgLower.includes("aborterror");
155-if (looksLikeTimeout) {
172+const msg = normalizeErrorMessage(err);
173+const kind = classifyBrowserFetchFailure(err);
174+if (kind === "timeout") {
175+return new Error(
176+ `Can't reach the OpenClaw browser control service (timed out after ${timeoutMs}ms). ${operatorHint}`,
177+ err instanceof Error ? { cause: err } : undefined,
178+);
179+}
180+if (kind === "aborted") {
156181return new Error(
157-appendBrowserToolModelHint(
158-`Can't reach the OpenClaw browser control service (timed out after ${timeoutMs}ms). ${operatorHint}`,
159-),
182+`Browser control request was cancelled. ${operatorHint}`,
183+err instanceof Error ? { cause: err } : undefined,
160184);
161185}
162186return new Error(
163187appendBrowserToolModelHint(
164188`Can't reach the OpenClaw browser control service. ${operatorHint} (${msg})`,
165189),
190+err instanceof Error ? { cause: err } : undefined,
166191);
167192}
168193此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。