From ae4aa78d1a6af2f9006a5817258beb81ba51bd9c Mon Sep 17 00:00:00 2001 From: ding113 Date: Sun, 22 Feb 2026 17:24:13 +0800 Subject: [PATCH 1/2] fix(proxy): persist Fake 200 error detail to DB/Redis and display in dashboard When upstream returns HTTP 200 with an error body (Fake 200), the detected error detail was only logged but not persisted. This caused the dashboard to show the raw detection code (e.g. "FAKE_200_JSON_ERROR_MESSAGE_NON_EMPTY") instead of the actual upstream error text (e.g. "Invalid API key"). - Store "CODE: detail" in errorMessage for DB/Redis persistence - Show actual detail (not code) in LogicTraceTab provider chain - Extract code prefix in SummaryTab for correct i18n lookup --- .../error-details-dialog/components/SummaryTab.tsx | 6 ++++-- src/app/v1/_lib/proxy/response-handler.ts | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/app/[locale]/dashboard/logs/_components/error-details-dialog/components/SummaryTab.tsx b/src/app/[locale]/dashboard/logs/_components/error-details-dialog/components/SummaryTab.tsx index 1873d23ce..a4fba2095 100644 --- a/src/app/[locale]/dashboard/logs/_components/error-details-dialog/components/SummaryTab.tsx +++ b/src/app/[locale]/dashboard/logs/_components/error-details-dialog/components/SummaryTab.tsx @@ -69,9 +69,11 @@ export function SummaryTab({ specialSettings && specialSettings.length > 0 ? JSON.stringify(specialSettings, null, 2) : null; const isFake200PostStreamFailure = typeof errorMessage === "string" && errorMessage.startsWith("FAKE_200_"); + const fake200Code = + isFake200PostStreamFailure && errorMessage ? errorMessage.split(": ")[0] : errorMessage; const fake200Reason = - isFake200PostStreamFailure && typeof errorMessage === "string" - ? t(getFake200ReasonKey(errorMessage, "fake200Reasons")) + isFake200PostStreamFailure && fake200Code + ? t(getFake200ReasonKey(fake200Code, "fake200Reasons")) : null; return ( diff --git a/src/app/v1/_lib/proxy/response-handler.ts b/src/app/v1/_lib/proxy/response-handler.ts index f0eed673b..c36ae3b0f 100644 --- a/src/app/v1/_lib/proxy/response-handler.ts +++ b/src/app/v1/_lib/proxy/response-handler.ts @@ -185,7 +185,7 @@ async function finalizeDeferredStreamingFinalizationIfNeeded( } else { effectiveStatusCode = 502; } - errorMessage = detected.code; + errorMessage = detected.detail ? `${detected.code}: ${detected.detail}` : detected.code; } else if (!streamEndedNormally) { effectiveStatusCode = clientAborted ? 499 : 502; errorMessage = clientAborted ? "CLIENT_ABORTED" : (abortReason ?? "STREAM_ABORTED"); @@ -330,7 +330,7 @@ async function finalizeDeferredStreamingFinalizationIfNeeded( attemptNumber: meta.attemptNumber, statusCode: effectiveStatusCode, statusCodeInferred, - errorMessage: detected.code, + errorMessage: detected.detail ?? detected.code, }); return { effectiveStatusCode, errorMessage, providerIdForPersistence }; From 13563c977cc9425b49018296fc2ac928e6726be9 Mon Sep 17 00:00:00 2001 From: ding113 Date: Sun, 22 Feb 2026 17:31:27 +0800 Subject: [PATCH 2/2] fix(ui): use consistent "CODE: detail" format in provider chain and handle in popover The previous commit used detected.detail ?? detected.code for the provider chain errorMessage, which stripped the FAKE_200_ prefix and broke the startsWith("FAKE_200_") checks in provider-chain-popover.tsx. - Use "CODE: detail" format in addProviderToChain (consistent with DB) - Extract code prefix via split(": ")[0] in popover for i18n lookup - Extract code prefix in fake200CodeForDisplay for warning banner --- .../logs/_components/provider-chain-popover.tsx | 10 ++++++---- src/app/v1/_lib/proxy/response-handler.ts | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/app/[locale]/dashboard/logs/_components/provider-chain-popover.tsx b/src/app/[locale]/dashboard/logs/_components/provider-chain-popover.tsx index 585b7836b..4f27ca0ae 100644 --- a/src/app/[locale]/dashboard/logs/_components/provider-chain-popover.tsx +++ b/src/app/[locale]/dashboard/logs/_components/provider-chain-popover.tsx @@ -131,9 +131,11 @@ export function ProviderChainPopover({ const hasFake200PostStreamFailure = chain.some( (item) => typeof item.errorMessage === "string" && item.errorMessage.startsWith("FAKE_200_") ); - const fake200CodeForDisplay = chain.find( - (item) => typeof item.errorMessage === "string" && item.errorMessage.startsWith("FAKE_200_") - )?.errorMessage; + const fake200CodeForDisplay = chain + .find( + (item) => typeof item.errorMessage === "string" && item.errorMessage.startsWith("FAKE_200_") + ) + ?.errorMessage?.split(": ")[0]; // Calculate actual request count (excluding intermediate states) const requestCount = chain.filter(isActualRequest).length; @@ -545,7 +547,7 @@ export function ProviderChainPopover({ {t("logs.details.fake200DetectedReason", { reason: t( getFake200ReasonKey( - item.errorMessage, + item.errorMessage.split(": ")[0], "logs.details.fake200Reasons" ) ), diff --git a/src/app/v1/_lib/proxy/response-handler.ts b/src/app/v1/_lib/proxy/response-handler.ts index c36ae3b0f..b533b240e 100644 --- a/src/app/v1/_lib/proxy/response-handler.ts +++ b/src/app/v1/_lib/proxy/response-handler.ts @@ -330,7 +330,7 @@ async function finalizeDeferredStreamingFinalizationIfNeeded( attemptNumber: meta.attemptNumber, statusCode: effectiveStatusCode, statusCodeInferred, - errorMessage: detected.detail ?? detected.code, + errorMessage: detected.detail ? `${detected.code}: ${detected.detail}` : detected.code, }); return { effectiveStatusCode, errorMessage, providerIdForPersistence };