[Bugfix] Raise VLLMValidationError for non-integer logit_bias keys (#46612)
Signed-off-by: muhammadfawaz1 <135441198+muhammadfawaz1@users.noreply.github.com> Co-authored-by: Mahad Durrani <114791389+mahadrehmann@users.noreply.github.com>
Muhammad Fawaz committed
Jun 30, 2026 at 11:18 UTC
97b5ce5c3931173b3efebca58e597b8e58730cd3
2 files changed
+82
-6
tests/entrypoints/openai/chat_completion/test_chat_logit_bias_validation.py
+57
@@ -76,3 +76,60 @@ async def test_chat_logit_bias_invalid(client):
76
assert error.status_code == 400
77
assert str(invalid_token_id) in error_message
78
assert str(vocab_size) in error_message
79
+
80
+
81
+@pytest.mark.asyncio
82
+async def test_chat_logit_bias_non_integer_key(client):
83
+ """Test that a non-integer logit_bias key is rejected with a clean,
84
+ informative error instead of a raw 'invalid literal for int()' message."""
85
+ with pytest.raises(openai.BadRequestError) as excinfo:
86
+ await client.chat.completions.create(
87
+ model=MODEL_NAME,
88
+ messages=[{"role": "user", "content": "Testing invalid logit bias key"}],
89
+ max_tokens=5,
90
+ logit_bias={"not_a_token_id": 50},
91
+ )
92
+
93
+ error = excinfo.value
94
+ error_message = str(error)
95
+
96
+ assert error.status_code == 400
97
+ assert "not_a_token_id" in error_message
98
+ assert "logit_bias" in error_message
99
+
100
+
101
+@pytest.mark.asyncio
102
+async def test_chat_logit_bias_non_numeric_value(client):
103
+ """Test that a non-numeric logit_bias value is rejected with a message
104
+ that names the specific offending token, not just a generic TypeError."""
105
+ with pytest.raises(openai.BadRequestError) as excinfo:
106
+ await client.chat.completions.create(
107
+ model=MODEL_NAME,
108
+ messages=[{"role": "user", "content": "Testing invalid logit bias value"}],
109
+ max_tokens=5,
110
+ logit_bias={"1": "not_a_number"},
111
+ )
112
+
113
+ error = excinfo.value
114
+ error_message = str(error)
115
+
116
+ assert error.status_code == 400
117
+ assert "logit_bias" in error_message
118
+
119
+
120
+@pytest.mark.asyncio
121
+async def test_chat_logit_bias_multiple_non_integer_keys(client):
122
+ """Test that ALL invalid logit_bias keys are reported together,
123
+ not just the first one encountered."""
124
+ with pytest.raises(openai.BadRequestError) as excinfo:
125
+ await client.chat.completions.create(
126
+ model=MODEL_NAME,
127
+ messages=[{"role": "user", "content": "Testing multiple bad keys"}],
128
+ max_tokens=5,
129
+ logit_bias={"bad1": 50.0, "bad2": 20.0},
130
+ )
131
+
132
+ error_message = str(excinfo.value)
133
+ assert excinfo.value.status_code == 400
134
+ assert "bad1" in error_message
135
+ assert "bad2" in error_message
vllm/sampling_params.py
+25
-6
@@ -385,12 +385,31 @@ class SamplingParams(
385
repetition_detection: RepetitionDetectionParams | None = None,
386
) -> "SamplingParams":
387
if logit_bias is not None:
388
- # Convert token_id to integer
389
- # Clamp the bias between -100 and 100 per OpenAI API spec
390
- logit_bias = {
391
- int(token): min(100.0, max(-100.0, bias))
392
- for token, bias in logit_bias.items()
393
- }
388
+ # Fast path uses a dict comprehension; on failure we iterate once
389
+ # to identify the exact offending entry for the error message.
390
+ try:
391
+ logit_bias = {
392
+ int(token): min(100.0, max(-100.0, bias))
393
+ for token, bias in logit_bias.items()
394
+ }
395
+ except (ValueError, TypeError):
396
+ invalid_keys = []
397
+ converted_logit_bias = {}
398
+ for token, bias in logit_bias.items():
399
+ try:
400
+ token_id = int(token)
401
+ except (ValueError, TypeError):
402
+ invalid_keys.append(token)
403
+ continue
404
+ converted_logit_bias[token_id] = min(100.0, max(-100.0, bias))
405
+ if invalid_keys:
406
+ raise VLLMValidationError(
407
+ f"logit_bias contains key(s) that cannot be "
408
+ f"converted to integer token IDs: {invalid_keys!r}",
409
+ parameter="logit_bias",
410
+ value=invalid_keys,
411
+ ) from None
412
+ logit_bias = converted_logit_bias
413
414
return SamplingParams(
415
n=1 if n is None else n,