From cf7320e4f2806888104b7bb934d3652d98c60f08 Mon Sep 17 00:00:00 2001 From: Ting Sun Date: Thu, 18 Jun 2026 19:26:10 +0800 Subject: [PATCH] Raise a clear error for empty token lists in bad_words_ids / sequence_bias NoBadWordsLogitsProcessor and the list form of SequenceBiasLogitsProcessor accept an empty inner token list and only fail later, during generation, with an opaque IndexError instead of a clear validation error at construction time. The empty list is reachable from the documented usage, since some words tokenize to an empty list (e.g. tokenizer("", add_special_tokens=False).input_ids == []). The dict form of sequence_bias already rejects empty sequences with a clear ValueError; this adds the same check to the two paths that missed it (the vacuous all/any over an empty list let it slip through), so an empty token list now raises a clear ValueError at construction. Signed-off-by: Ting Sun --- src/transformers/generation/logits_process.py | 3 +++ tests/generation/test_logits_process.py | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/transformers/generation/logits_process.py b/src/transformers/generation/logits_process.py index f3c1748361d2..c0d9e7ec0de1 100644 --- a/src/transformers/generation/logits_process.py +++ b/src/transformers/generation/logits_process.py @@ -1376,6 +1376,7 @@ def _validate_arguments(self): def all_token_bias_pairs_are_valid(sequence): return ( isinstance(sequence[0], list) + and len(sequence[0]) > 0 and all(isinstance(token_id, (int, np.integer)) and token_id > 0 for token_id in sequence[0]) and isinstance(sequence[1], float) ) @@ -1477,6 +1478,8 @@ def _validate_arguments(self): raise ValueError(f"`bad_words_ids` has to be a non-empty list, but is {bad_words_ids}.") if any(not isinstance(bad_word_ids, list) for bad_word_ids in bad_words_ids): raise ValueError(f"`bad_words_ids` has to be a list of lists, but is {bad_words_ids}.") + if any(len(bad_word_ids) == 0 for bad_word_ids in bad_words_ids): + raise ValueError(f"`bad_words_ids` has to be a list of non-empty lists, but is {bad_words_ids}.") if any( any((not isinstance(token_id, (int, np.integer)) or token_id < 0) for token_id in bad_word_ids) for bad_word_ids in bad_words_ids diff --git a/tests/generation/test_logits_process.py b/tests/generation/test_logits_process.py index 83f170a4d555..76610baa4cb2 100644 --- a/tests/generation/test_logits_process.py +++ b/tests/generation/test_logits_process.py @@ -743,6 +743,17 @@ def test_no_bad_words_dist_processor(self): filtered_scores = no_bad_words_dist_proc(input_ids, scores) torch.testing.assert_close(scores, filtered_scores, rtol=1e-3, atol=1e-3) + def test_bad_words_and_sequence_bias_reject_empty_token_list(self): + # An empty token list (e.g. from `tokenizer("", add_special_tokens=False).input_ids`) must be rejected at + # construction with a clear `ValueError`, rather than crashing later with an `IndexError` during generation. + eos_token_id = 4 + with self.assertRaises(ValueError): + NoBadWordsLogitsProcessor(bad_words_ids=[[]], eos_token_id=eos_token_id) + with self.assertRaises(ValueError): + NoBadWordsLogitsProcessor(bad_words_ids=[[], [3]], eos_token_id=eos_token_id) + with self.assertRaises(ValueError): + SequenceBiasLogitsProcessor(sequence_bias=[[[], 1.0]]) + def test_bias_dist_processor(self): vocab_size = 5 batch_size = 2