apache/beam · error · ValueError

Cannot use an unkeyed model handler with pre or postprocessi

Error message

Cannot use an unkeyed model handler with pre or postprocessing functions defined in a keyed model handler. All pre/postprocessing functions must be defined on the outer modelhandler.

What it means

The multi-model (list) constructor of KeyedModelHandler accepts handler/key tuples; each inner handler must be free of preprocess/postprocess functions because they must be defined on the outer keyed handler. A ValueError is raised for any tuple whose handler has decorated functions.

Source

Thrown at sdks/python/apache_beam/ml/inference/base.py:785

            'pre/postprocessing functions must be defined on the outer model'
            'handler.')
      self._env_vars = getattr(unkeyed, '_env_vars', {})
      self._unkeyed = unkeyed
      return

    self._max_models_per_worker_hint = max_models_per_worker_hint
    # To maintain an efficient representation, we will map all keys in a given
    # KeyModelMapping to a single id (the first key in the KeyModelMapping
    # list). We will then map that key to a ModelHandler. This will allow us to
    # quickly look up the appropriate ModelHandler for any given key.
    self._id_to_mh_map: dict[str, ModelHandler[ExampleT, PredictionT,
                                               ModelT]] = {}
    self._key_to_id_map: dict[str, str] = {}
    for mh_tuple in unkeyed:
      mh = mh_tuple.mh
      keys = mh_tuple.keys
      if len(mh.get_preprocess_fns()) or len(mh.get_postprocess_fns()):
        raise ValueError(
            'Cannot use an unkeyed model handler with pre or '
            'postprocessing functions defined in a keyed model handler. All '
            'pre/postprocessing functions must be defined on the outer model'
            'handler.')
      hints = mh.get_resource_hints()
      if len(hints) > 0:
        logging.warning(
            'mh %s defines the following resource hints, which will be'
            'ignored: %s. Resource hints are not respected when more than one '
            'model handler is used in a KeyedModelHandler. If you would like '
            'to specify resource hints, you can do so by overriding the '
            'KeyedModelHandler.get_resource_hints() method.',
            mh,
            hints)
      batch_kwargs = mh.batch_elements_kwargs()
      if len(batch_kwargs) > 0:
        logging.warning(
            'mh %s defines the following batching kwargs which will be '

View on GitHub (pinned to 12126d8942)

Solutions

  1. Strip the pre/postprocessing functions from each inner handler (use undecorated instances).
  2. Attach the functions once on the resulting KeyedModelHandler via with_preprocess_fns/with_postprocess_fns.
  3. If per-model preprocessing is truly needed, implement it inside a custom ModelHandler subclass instead.

Example fix

# before
mh_a = HandlerA().with_preprocess_fns(fn)
keyed = KeyedModelHandler([KeyedModelHandlerTuple(mh_a, ['a'])])

# after
keyed = KeyedModelHandler([KeyedModelHandlerTuple(HandlerA(), ['a'])]).with_preprocess_fns(fn)
Defensive patterns

Strategy: validation

Validate before calling

bad = [mh for mh, _ in [(t.mh, t.keys) for t in tuples] if mh.get_preprocess_fns() or mh.get_postprocess_fns()]
assert not bad, f'Inner handlers with pre/post fns: {bad}'

Type guard

def all_undecorated(tuples):
    return all(not t.mh.get_preprocess_fns() and not t.mh.get_postprocess_fns() for t in tuples)

Try / catch

try:
    keyed = KeyedModelHandler(tuples)
except ValueError as e:
    if 'pre or postprocessing' in str(e):
        tuples = [KeyedModelHandlerTuple(strip_fns(t.mh), t.keys) for t in tuples]
        keyed = KeyedModelHandler(tuples).with_preprocess_fns(fn)
    else:
        raise

Prevention

When it happens

Trigger: KeyedModelHandler([KeyedModelHandlerTuple(mh1, ['k1']), KeyedModelHandlerTuple(mh2, ['k2'])]) where mh1 or mh2 has pre/postprocess fns attached via with_preprocess_fns/with_postprocess_fns.

Common situations: Building multi-model serving setups where each cohort's handler was individually decorated; refactoring decorated handlers into keyed multi-handler configs without moving the functions outward.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/58a638185ca4d315. Report an issue: GitHub.