Skip to content

Commit 95e94d3

Browse files
CopilotJoshFerge
andcommitted
Fix integration proxy endpoint to properly forward upstream error status codes
Fixes SENTRY-5K7H When the integration proxy endpoint (Relay's forward endpoint) forwards requests to upstream services like GitHub and those services return error status codes (e.g. 403 Forbidden due to IP allow-list restrictions), Relay was converting those errors to 500 Internal Server Error responses instead of passing through the actual status code. The root cause was in `UpstreamRequestError::IntoResponse` where the wildcard catch-all `_ => StatusCode::INTERNAL_SERVER_ERROR` was matching `ResponseError(status, _)` and `RateLimited(_)` variants, discarding the actual upstream status code. Fix: Explicitly handle `ResponseError` and `RateLimited` before the wildcard so that: - `ResponseError(status, _)` returns the actual upstream HTTP status code - `RateLimited(_)` returns 429 Too Many Requests - Internal errors (`NoCredentials`, `ChannelClosed`, `AuthDenied`) still return 500 via the wildcard Co-authored-by: JoshFerge <1976777+JoshFerge@users.noreply.github.com>
1 parent 5b0864d commit 95e94d3

File tree

2 files changed

+28
-0
lines changed

2 files changed

+28
-0
lines changed

relay-server/src/services/upstream.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,11 @@ impl IntoResponse for UpstreamRequestError {
226226
StatusCode::BAD_GATEWAY.into_response()
227227
}
228228
}
229+
// Proxy the upstream status code so clients receive the actual error (e.g. a 403
230+
// from GitHub) rather than a generic 500.
231+
Self::RateLimited(_) => StatusCode::TOO_MANY_REQUESTS.into_response(),
232+
Self::ResponseError(status, _) => status.into_response(),
233+
// NoCredentials, ChannelClosed, and AuthDenied are all internal errors.
229234
_ => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
230235
}
231236
}

tests/integration/test_forwarding.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,26 @@ def hi():
118118

119119
response = relay.get("/api/test/timeout")
120120
assert response.status_code == 504
121+
122+
123+
@pytest.mark.parametrize(
124+
"upstream_status",
125+
[403, 404, 429, 500, 503],
126+
ids=["forbidden", "not_found", "rate_limited", "server_error", "service_unavailable"],
127+
)
128+
def test_forwarding_error_status_codes(upstream_status, mini_sentry, relay):
129+
"""Test that Relay forwards error status codes from the upstream without converting them to 500."""
130+
mini_sentry.fail_on_relay_error = False
131+
132+
@mini_sentry.app.route("/api/test/error-status")
133+
def error_response():
134+
return Response(
135+
b'{"detail": "upstream error"}',
136+
status=upstream_status,
137+
content_type="application/json",
138+
)
139+
140+
relay = relay(mini_sentry)
141+
142+
response = relay.get("/api/test/error-status")
143+
assert response.status_code == upstream_status

0 commit comments

Comments
 (0)