fix(failover): add missing network errno patterns to text-based timeout classifier (#42830)
Merged via squash. Prepared head SHA: 91761487e8825c0fd6582a762d04bba04f726a85 Co-authored-by: jnMetaCode <12096460+jnMetaCode@users.noreply.github.com> Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com> Reviewed-by: @altaywtf
This commit is contained in:
parent
a6711afdc2
commit
f640326e31
@ -225,6 +225,7 @@ Docs: https://docs.openclaw.ai
|
||||
- macOS/browser proxy: serialize non-GET browser proxy request bodies through `AnyCodable.foundationValue` so nested JSON bodies no longer crash the macOS app with `Invalid type in JSON write (__SwiftValue)`. (#43069) Thanks @Effet.
|
||||
- CLI/skills tables: keep terminal table borders aligned for wide graphemes, use full reported terminal width, and switch a few ambiguous skill icons to Terminal-safe emoji so `openclaw skills` renders more consistently in Terminal.app and iTerm. Thanks @vincentkoc.
|
||||
- Memory/Gemini: normalize returned Gemini embeddings across direct query, direct batch, and async batch paths so memory search uses consistent vector handling for Gemini too. (#43409) Thanks @gumadeiras.
|
||||
- Agents/failover: recognize additional serialized network errno strings plus `EHOSTDOWN` and `EPIPE` structured codes so transient transport failures trigger timeout failover more reliably. (#42830) Thanks @jnMetaCode.
|
||||
|
||||
## 2026.3.7
|
||||
|
||||
|
||||
@ -274,6 +274,8 @@ describe("failover-error", () => {
|
||||
it("infers timeout from common node error codes", () => {
|
||||
expect(resolveFailoverReasonFromError({ code: "ETIMEDOUT" })).toBe("timeout");
|
||||
expect(resolveFailoverReasonFromError({ code: "ECONNRESET" })).toBe("timeout");
|
||||
expect(resolveFailoverReasonFromError({ code: "EHOSTDOWN" })).toBe("timeout");
|
||||
expect(resolveFailoverReasonFromError({ code: "EPIPE" })).toBe("timeout");
|
||||
});
|
||||
|
||||
it("infers timeout from abort/error stop-reason messages", () => {
|
||||
|
||||
@ -170,7 +170,9 @@ export function resolveFailoverReasonFromError(err: unknown): FailoverReason | n
|
||||
"ECONNREFUSED",
|
||||
"ENETUNREACH",
|
||||
"EHOSTUNREACH",
|
||||
"EHOSTDOWN",
|
||||
"ENETRESET",
|
||||
"EPIPE",
|
||||
"EAI_AGAIN",
|
||||
].includes(code)
|
||||
) {
|
||||
|
||||
@ -535,6 +535,23 @@ describe("isFailoverErrorMessage", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("matches network errno codes in serialized error messages", () => {
|
||||
const samples = [
|
||||
"Error: connect ETIMEDOUT 10.0.0.1:443",
|
||||
"Error: connect ESOCKETTIMEDOUT 10.0.0.1:443",
|
||||
"Error: connect EHOSTUNREACH 10.0.0.1:443",
|
||||
"Error: connect ENETUNREACH 10.0.0.1:443",
|
||||
"Error: write EPIPE",
|
||||
"Error: read ENETRESET",
|
||||
"Error: connect EHOSTDOWN 192.168.1.1:443",
|
||||
];
|
||||
for (const sample of samples) {
|
||||
expect(isTimeoutErrorMessage(sample)).toBe(true);
|
||||
expect(classifyFailoverReason(sample)).toBe("timeout");
|
||||
expect(isFailoverErrorMessage(sample)).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it("does not classify MALFORMED_FUNCTION_CALL as timeout", () => {
|
||||
const sample = "Unhandled stop reason: MALFORMED_FUNCTION_CALL";
|
||||
expect(isTimeoutErrorMessage(sample)).toBe(false);
|
||||
|
||||
@ -37,6 +37,13 @@ const ERROR_PATTERNS = {
|
||||
"fetch failed",
|
||||
"socket hang up",
|
||||
/\beconn(?:refused|reset|aborted)\b/i,
|
||||
/\benetunreach\b/i,
|
||||
/\behostunreach\b/i,
|
||||
/\behostdown\b/i,
|
||||
/\benetreset\b/i,
|
||||
/\betimedout\b/i,
|
||||
/\besockettimedout\b/i,
|
||||
/\bepipe\b/i,
|
||||
/\benotfound\b/i,
|
||||
/\beai_again\b/i,
|
||||
/without sending (?:any )?chunks?/i,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user