PaddlePaddle/PaddleOCR · error · ValueError

`{name}` and `{new_name}` are mutually exclusive.

Error message

`{name}` and `{new_name}` are mutually exclusive.

What it means

PaddleOCR's constructor accepts deprecated parameter names (mapped via _DEPRECATED_PARAM_NAME_MAPPING, e.g. det_model_dir -> text_detection_model_dir, use_angle_cls -> use_textline_orientation). If you pass BOTH the deprecated name and its modern replacement, and the modern one is not None, it raises ValueError because the two would conflict. A deprecation warning is emitted first, then the conflict is detected.

Source

Thrown at paddleocr/_pipelines/ocr.py:164

            "text_det_limit_type": text_det_limit_type,
            "text_det_thresh": text_det_thresh,
            "text_det_box_thresh": text_det_box_thresh,
            "text_det_unclip_ratio": text_det_unclip_ratio,
            "text_det_input_shape": text_det_input_shape,
            "text_rec_score_thresh": text_rec_score_thresh,
            "return_word_box": return_word_box,
            "text_rec_input_shape": text_rec_input_shape,
        }
        base_params = {}
        for name, val in kwargs.items():
            if name in _DEPRECATED_PARAM_NAME_MAPPING:
                new_name = _DEPRECATED_PARAM_NAME_MAPPING[name]
                warn_deprecated_param(name, new_name)
                assert (
                    new_name in params
                ), f"{repr(new_name)} is not a valid parameter name."
                if params[new_name] is not None:
                    raise ValueError(
                        f"`{name}` and `{new_name}` are mutually exclusive."
                    )
                params[new_name] = val
            else:
                base_params[name] = val

        self._params = params

        super().__init__(**base_params)

    @property
    def _paddlex_pipeline_name(self):
        return "OCR"

    def predict_iter(
        self,
        input,
        *,

View on GitHub (pinned to 2661c7c0ef)

Solutions

  1. Delete the deprecated parameter and keep only the modern one (see _DEPRECATED_PARAM_NAME_MAPPING for the exact rename table).
  2. If the modern param is at its default None, passing only the deprecated name still works — so just drop one of the two.
  3. Grep your config for the old names (det_model_dir, det_db_thresh, use_angle_cls, cls_model_dir, rec_batch_num, etc.) and migrate them.

Example fix

# before
ocr = PaddleOCR(use_angle_cls=True, use_textline_orientation=True)  # ValueError
# after
ocr = PaddleOCR(use_textline_orientation=True)
Defensive patterns

Strategy: validation

Validate before calling

from paddleocr._pipelines.ocr import _DEPRECATED_PARAM_NAME_MAPPING

def strip_deprecated_conflicts(cfg: dict) -> dict:
    cfg = dict(cfg)
    for old, new in _DEPRECATED_PARAM_NAME_MAPPING.items():
        if old in cfg and cfg.get(new) is not None:
            del cfg[old]  # prefer the modern name
    return cfg

Prevention

When it happens

Trigger: PaddleOCR(use_angle_cls=True, use_textline_orientation=True), PaddleOCR(det_model_dir='...', text_detection_model_dir='...'), or any pair in the mapping such as rec_batch_num together with text_recognition_batch_size where the new-name value is set.

Common situations: Migrating old sample code incrementally and ending up with both old and new kwargs; config files that accumulate options across paddleocr upgrades; tutorials mixing v2.x and v3.x parameter styles.

Related errors


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