From 4c5b932e812e5cf4ef0a257ab85825ce4dcd651b Mon Sep 17 00:00:00 2001 From: chiyi Date: Sun, 9 Aug 2026 13:40:08 +0800 Subject: [PATCH] =?UTF-8?q?fix(adobe):=20=E5=AA=92=E4=BD=93=E5=88=86?= =?UTF-8?q?=E7=B1=BB=E6=8E=A5=E5=8F=A3=E6=8A=93=E4=B8=8D=E5=88=B0=E5=8F=82?= =?UTF-8?q?=E8=80=83=E5=9B=BE=E7=9A=84=20400=20=E5=BD=92=E4=B8=BA=E4=B8=B4?= =?UTF-8?q?=E6=97=B6=E9=94=99=E8=AF=AF=EF=BC=8C=E8=87=AA=E5=8A=A8=E6=8D=A2?= =?UTF-8?q?=E5=8F=B7=E9=87=8D=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/provider/adobe/client.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/backend/internal/provider/adobe/client.go b/backend/internal/provider/adobe/client.go index e9f6e75..1315f38 100644 --- a/backend/internal/provider/adobe/client.go +++ b/backend/internal/provider/adobe/client.go @@ -74,6 +74,20 @@ func isContentRejection(status int, body string) bool { return strings.Contains(body, "unsafe") || strings.Contains(body, "privacy_error") } +// isClassifierGlitch reports whether a 400 comes from Adobe's media moderation +// backend failing to READ the media it was handed ("Gemini classification API +// returned HTTP 400 ... Cannot fetch content from the provided URL") rather than +// from the request being wrong. The blobs were just uploaded and the identical +// request succeeds on a retry, so this is a transient upstream fault: retrying +// (with freshly uploaded blobs) is the fix, and the account must not be blamed. +func isClassifierGlitch(status int, body string) bool { + if status != 400 { + return false + } + return strings.Contains(body, "media_classification_client_error") || + strings.Contains(body, "classification API returned") +} + var profileURLs = []string{ "https://ims-na1.adobelogin.com/ims/profile/v1", "https://adobeid-na1.services.adobe.com/ims/profile/v1", @@ -667,6 +681,9 @@ func (c *Client) pollImage(ctx context.Context, sess *tlsSession, token, pollURL if resp.StatusCode == 429 || resp.StatusCode == 451 || resp.StatusCode >= 500 { return nil, nil, fmt.Errorf("%w (poll %d: %s)", ErrDeadUpstream, resp.StatusCode, clip(body, 300)) } + if isClassifierGlitch(resp.StatusCode, string(body)) { + return nil, nil, fmt.Errorf("%w (poll classifier: %s)", ErrTemporaryUpstream, clip(body, 300)) + } if resp.StatusCode != 200 { return nil, nil, fmt.Errorf("adobe poll failed: %d %s", resp.StatusCode, clip(body, 300)) } @@ -867,6 +884,9 @@ func (c *Client) pollVideo(ctx context.Context, sess *tlsSession, token, pollURL if resp.StatusCode == 429 || resp.StatusCode == 451 || resp.StatusCode >= 500 { return nil, nil, fmt.Errorf("%w (video poll %d: %s)", ErrDeadUpstream, resp.StatusCode, clip(body, 300)) } + if isClassifierGlitch(resp.StatusCode, string(body)) { + return nil, nil, fmt.Errorf("%w (video poll classifier: %s)", ErrTemporaryUpstream, clip(body, 300)) + } if resp.StatusCode != 200 { return nil, nil, fmt.Errorf("adobe video poll failed: %d %s", resp.StatusCode, clip(body, 300)) }