PaddlePaddle/PaddleOCR · error · ValueError
If `eos_token_id` is defined, make sure that `pad_token_id`
Error message
If `eos_token_id` is defined, make sure that `pad_token_id` is defined.
What it means
During greedy decoding, once sequences finish they are replaced by pad tokens (next_tokens * unfinished + pad * finished). If eos_token_id is configured but pad_token_id is None, there is no value to pad finished sequences with, so the loop raises.
Source
Thrown at ppocr/modeling/heads/rec_ppformulanet_head.py:1140
decoder_attention_mask=decoder_attention_mask,
encoder_outputs=encoder_outputs,
past_key_values=past_key_values,
return_dict=True,
output_attentions=False,
output_hidden_states=False,
)
if use_parallel:
next_token_logits = outputs.logits[:, -parallel_step:, :]
else:
next_token_logits = outputs.logits[:, -1, :]
next_tokens_scores = self.logits_processor(input_ids, next_token_logits)
next_tokens = paddle.argmax(next_tokens_scores, axis=-1)
if eos_token_id is not None:
# False
if pad_token_id is None:
raise ValueError(
"If `eos_token_id` is defined, make sure that `pad_token_id` is defined."
)
next_tokens = next_tokens * unfinished_sequences + pad_token_id * (
1 - unfinished_sequences
)
if use_parallel:
input_ids = paddle.concat([input_ids, next_tokens], axis=-1)
decoder_input_ids = next_tokens
else:
input_ids = paddle.concat(
[input_ids, next_tokens.unsqueeze(1)], axis=-1
)
decoder_input_ids = next_tokens.unsqueeze(1)
past_length = past_key_values[0][0].shape[2]
past_key_values = outputs.past_key_values
cache_position = cache_position[-1:] + 1View on GitHub (pinned to 2661c7c0ef)
Solutions
- Set pad_token_id in the model config (any token id safe to fill with, commonly 0 or the tokenizer's pad)
- Or pass pad_token_id explicitly to the generate call alongside eos_token_id
- If your vocab truly has no pad, allocate a dedicated unused id as pad
Example fix
# before config.eos_token_id = 2 # pad_token_id stays None # after config.eos_token_id = 2 config.pad_token_id = 0
Defensive patterns
Strategy: validation
Validate before calling
def check_gen_tokens(eos_token_id, pad_token_id):
if eos_token_id is not None and pad_token_id is None:
raise ValueError('pad_token_id must be set when eos_token_id is set')
return eos_token_id, pad_token_id
# check_gen_tokens(cfg.get('eos_token_id'), cfg.get('pad_token_id')) Type guard
def token_config_ok(cfg) -> bool:
return cfg.get('eos_token_id') is None or cfg.get('pad_token_id') is not None Try / catch
null # configuration error; validate before generation rather than mid-loop catching
Prevention
- Set pad_token_id in every config that sets eos_token_id
- Add a pre-flight check in your inference entry point
- Verify pad/eos ids exist in the tokenizer vocab after any vocab surgery
When it happens
Trigger: Running generate/greedy_search with a config that sets eos_token_id but leaves pad_token_id unset (None), and eos_token_id is not None at the call site.
Common situations: Custom tokenizers or trimmed vocab configs where pad was removed; porting a config from sample() usage where pad was irrelevant; overriding eos_token_id per-call while forgetting pad_token_id.
Related errors
- `decoder_start_token_id` expected to have length {batch_size
- The filter_logits_fn is not supported
- Make sure that when passing `sliding_window` that its value
- This attention mask converter is causal. Make sure to pass `
- Sliding window is currently only implemented for causal mask
AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14).
Data as JSON: /api/errors/20d09158c5121e22.
Report an issue: GitHub.