From c7b2f8ee3a1eb501caaed457ec25de2f1005a904 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Fri, 26 Jun 2026 11:04:08 +0800 Subject: [PATCH] fix(acp): treat numeric gateway codes as unknown-method so session.prepare falls back A gateway that doesn't implement xworkmate.session.prepare returns an "unknown method: xworkmate.session.prepare" error. isOpenClawUnknownMethodError gated on a string code allowlist {"", INVALID_REQUEST, METHOD_NOT_FOUND}, but real gateways send a numeric JSON-RPC code (e.g. -32002 / -32601) which shared.StringArg stringifies to "-32002". The matcher then returned false, so the graceful fallback (openClawFallbackSessionPreparePayload) never fired and every turn hard-failed with "-32002: unknown method: xworkmate.session.prepare". Match on the unambiguous message ("unknown method" + the method name) instead of the stringified numeric code. Add a regression test covering numeric codes and guarding against swallowing unrelated errors / other method names. Co-Authored-By: Claude Opus 4.8 --- internal/acp/orchestrator.go | 12 +++-- .../acp/orchestrator_normalize_result_test.go | 47 +++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/internal/acp/orchestrator.go b/internal/acp/orchestrator.go index da33adb..4de7330 100644 --- a/internal/acp/orchestrator.go +++ b/internal/acp/orchestrator.go @@ -613,13 +613,19 @@ func (o *SessionOrchestrator) openClawArtifactPrepare( func isOpenClawUnknownMethodError(errorPayload map[string]any, method string) bool { message := strings.ToLower(strings.TrimSpace(shared.StringArg(errorPayload, "message", ""))) - code := strings.ToUpper(strings.TrimSpace(shared.StringArg(errorPayload, "code", ""))) if message == "" { return false } + // 消息形如「unknown method: 」已明确指向「网关不认识该方法」,足以判定, + // 据此走 graceful fallback(如 openClawFallbackSessionPreparePayload)。 + // + // 注意:不能再用严格的 code 白名单来 gate。真实网关常以数字 JSON-RPC code + // (-32601 method not found / -32600 invalid request / -32002 等) 回传, + // 经 shared.StringArg(fmt.Sprint) 会被字符串化为 "-32601"/"-32002", + // 旧实现只接受 {"", INVALID_REQUEST, METHOD_NOT_FOUND},导致 fallback 失效、 + // session.prepare 直接以 -32002 硬失败整轮任务。 return strings.Contains(message, "unknown method") && - strings.Contains(message, strings.ToLower(strings.TrimSpace(method))) && - (code == "" || code == "INVALID_REQUEST" || code == "METHOD_NOT_FOUND") + strings.Contains(message, strings.ToLower(strings.TrimSpace(method))) } func openClawFallbackSessionPreparePayload(params map[string]any) map[string]any { diff --git a/internal/acp/orchestrator_normalize_result_test.go b/internal/acp/orchestrator_normalize_result_test.go index e4e9372..4536dd5 100644 --- a/internal/acp/orchestrator_normalize_result_test.go +++ b/internal/acp/orchestrator_normalize_result_test.go @@ -282,3 +282,50 @@ func TestTaskGetArtifactExportReceivesRequiredArtifactExtensions(t *testing.T) { t.Fatalf("expected expectedFileCountByExtension to reach export, got %#v", exportParams) } } + +func TestIsOpenClawUnknownMethodErrorAcceptsNumericGatewayCodes(t *testing.T) { + const method = "xworkmate.session.prepare" + cases := []struct { + name string + payload map[string]any + want bool + }{ + { + name: "string invalid_request code", + payload: map[string]any{"code": "INVALID_REQUEST", "message": "unknown method: xworkmate.session.prepare"}, + want: true, + }, + { + name: "numeric -32002 (real gateway shape that previously hard-failed)", + payload: map[string]any{"code": float64(-32002), "message": "unknown method: xworkmate.session.prepare"}, + want: true, + }, + { + name: "numeric -32601 method not found", + payload: map[string]any{"code": float64(-32601), "message": "Unknown method: xworkmate.session.prepare"}, + want: true, + }, + { + name: "empty code", + payload: map[string]any{"message": "unknown method: xworkmate.session.prepare"}, + want: true, + }, + { + name: "unrelated error must not be swallowed", + payload: map[string]any{"code": float64(-32002), "message": "gateway socket closed"}, + want: false, + }, + { + name: "unknown method for a different method name", + payload: map[string]any{"code": float64(-32601), "message": "unknown method: chat.send"}, + want: false, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if got := isOpenClawUnknownMethodError(tc.payload, method); got != tc.want { + t.Fatalf("isOpenClawUnknownMethodError(%v) = %v, want %v", tc.payload, got, tc.want) + } + }) + } +}