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:] + 1

View on GitHub (pinned to 2661c7c0ef)

Solutions

  1. Set pad_token_id in the model config (any token id safe to fill with, commonly 0 or the tokenizer's pad)
  2. Or pass pad_token_id explicitly to the generate call alongside eos_token_id
  3. 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

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


AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14). Data as JSON: /api/errors/20d09158c5121e22. Report an issue: GitHub.