From 0bae9708a729943a26b5e08313f08aabc5f3cab9 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 5 Aug 2026 20:57:50 -0700 Subject: [PATCH] fix(arize_phoenix): lowercase OTLP/gRPC auth metadata key (#34883) --- litellm/integrations/arize/arize_phoenix.py | 3 +- .../integrations/arize/test_arize_phoenix.py | 40 +++++++++++++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/litellm/integrations/arize/arize_phoenix.py b/litellm/integrations/arize/arize_phoenix.py index e13fc0184a4..5b52c59cae2 100644 --- a/litellm/integrations/arize/arize_phoenix.py +++ b/litellm/integrations/arize/arize_phoenix.py @@ -430,7 +430,8 @@ class ArizePhoenixLogger(OpenTelemetry): otlp_auth_headers = None if api_key is not None: - otlp_auth_headers = f"Authorization=Bearer {api_key}" + auth_header_key = "authorization" if protocol == "otlp_grpc" else "Authorization" + otlp_auth_headers = f"{auth_header_key}=Bearer {api_key}" elif "app.phoenix.arize.com" in endpoint: raise ValueError("PHOENIX_API_KEY must be set when using Phoenix Cloud (app.phoenix.arize.com).") diff --git a/tests/test_litellm/integrations/arize/test_arize_phoenix.py b/tests/test_litellm/integrations/arize/test_arize_phoenix.py index afd83f81ce0..9f79534242c 100644 --- a/tests/test_litellm/integrations/arize/test_arize_phoenix.py +++ b/tests/test_litellm/integrations/arize/test_arize_phoenix.py @@ -37,8 +37,8 @@ class TestArizePhoenixConfig(unittest.TestCase): # Call the function to get the configuration config = ArizePhoenixLogger.get_arize_phoenix_config() - # Verify the configuration - now uses standard Authorization Bearer format - self.assertEqual(config.otlp_auth_headers, "Authorization=Bearer test_api_key") + # gRPC metadata keys must be lowercase, so the auth header key is lowercased + self.assertEqual(config.otlp_auth_headers, "authorization=Bearer test_api_key") self.assertEqual(config.endpoint, "grpc://test.endpoint") self.assertEqual(config.protocol, "otlp_grpc") @@ -136,7 +136,7 @@ class TestArizePhoenixConfig(unittest.TestCase): "PHOENIX_COLLECTOR_ENDPOINT": "grpc://localhost:6006", "PHOENIX_API_KEY": "test_api_key", }, - "Authorization=Bearer test_api_key", + "authorization=Bearer test_api_key", "grpc://localhost:6006", "otlp_grpc", id="explicit grpc endpoint with grpc:// prefix", @@ -215,6 +215,40 @@ def test_get_arize_phoenix_config_expection_on_missing_api_key(monkeypatch, env_ ArizePhoenixLogger.get_arize_phoenix_config() +@pytest.mark.parametrize( + "collector_endpoint, expected_key", + [ + pytest.param("grpc://localhost:6006", "authorization", id="grpc prefix"), + pytest.param("http://localhost:4317", "authorization", id="grpc port 4317"), + pytest.param("http://localhost:6006", "Authorization", id="http"), + ], +) +def test_get_arize_phoenix_config_auth_header_key_casing( + monkeypatch, collector_endpoint, expected_key +): + """Regression for #34882: gRPC metadata keys must be lowercase. + + HTTP headers are case-insensitive, but the OTLP/gRPC exporter rejects an + uppercase ``Authorization`` metadata key, so span export silently fails. + """ + for key in [ + "PHOENIX_API_KEY", + "PHOENIX_COLLECTOR_ENDPOINT", + "PHOENIX_COLLECTOR_HTTP_ENDPOINT", + ]: + monkeypatch.delenv(key, raising=False) + + monkeypatch.setenv("PHOENIX_API_KEY", "test_api_key") + monkeypatch.setenv("PHOENIX_COLLECTOR_ENDPOINT", collector_endpoint) + + config = ArizePhoenixLogger.get_arize_phoenix_config() + + assert config.otlp_auth_headers == f"{expected_key}=Bearer test_api_key" + header_key = config.otlp_auth_headers.split("=", 1)[0] + if config.protocol == "otlp_grpc": + assert header_key == header_key.lower() + + # --------------------------------------------------------------------------- # Per-project routing via Resource (not span attributes) # ---------------------------------------------------------------------------