apache/beam · error · RuntimeError

Invalid model update: sent many model paths to update, but…

Error message

Invalid model update: sent many model paths to update, but KeyedModelHandler is wrapping a single model.

What it means

update_model_paths on KeyedModelHandler supports updating models only when the handler wraps multiple handlers/cohorts. If it wraps a single model, receiving multiple (or any list-form) model paths is treated as an invalid update and a RuntimeError is raised, since there is no cohort structure to distribute them across.

Solutions

  1. Pass a single model path (or None) when the KeyedModelHandler wraps one model, matching its single-model mode.
  2. If multi-path updates are intended, wrap multiple handlers: KeyedModelHandler([KeyedModelHandlerTuple(...), ...]).
  3. Guard the update callback: check keyed_mh._single_model (or catch RuntimeError) and route single-model updates accordingly.

Example fix

# before
keyed = KeyedModelHandler(MyHandler(path))
keyed.update_model_paths(['new_a', 'new_b'])  # RuntimeError

# after
keyed = KeyedModelHandler([
  KeyedModelHandlerTuple(MyHandler(path), ['cohort_a']),
  KeyedModelHandlerTuple(MyHandler(path2), ['cohort_b'])])
keyed.update_model_paths(['new_a', 'new_b'])
Defensive patterns

Strategy: validation

Validate before calling

if getattr(keyed, '_single_model', True) and model_paths and len(model_paths) > 1:
    raise ValueError('Single-model KeyedModelHandler cannot accept multiple update paths')

Type guard

def supports_multi_path_update(keyed):
    return not getattr(keyed, '_single_model', True)

Try / catch

try:
    keyed.update_model_paths(model_paths)
except RuntimeError as e:
    if 'wrapping a single model' in str(e):
        keyed.update_model_paths([model_paths[0]])
    else:
        raise

Prevention

When it happens

Trigger: Calling keyed_mh.update_model_paths(paths) with a list of several paths on a KeyedModelHandler constructed from a single (non-list) handler; background model-refresh hook delivering multi-cohort updates to a single-model handler.

Common situations: Enabling automatic model refresh (model_manager / update_model_paths via a Watch/flush watcher) on a pipeline that later switched to a single unkeyed handler; config driven updates that always pass a list of candidate paths.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

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

  def validate_inference_args(self, inference_args: Optional[dict[str, Any]]):
    if self._single_model:
      return self._unkeyed.validate_inference_args(inference_args)
    for mh in self._id_to_mh_map.values():
      mh.validate_inference_args(inference_args)

  def update_model_paths(
      self,
      model: Union[ModelT, _ModelHandlerManager],
      model_paths: list[KeyModelPathMapping[KeyT]] = None):
    # When there are many models, the keyed model handler is responsible for
    # reorganizing the model handlers into cohorts and telling the model
    # manager to update every cohort's associated model handler. The model
    # manager is responsible for performing the updates and tracking which
    # updates have already been applied.
    if model_paths is None or len(model_paths) == 0 or model is None:
      return
    if self._single_model:
      raise RuntimeError(
          'Invalid model update: sent many model paths to '
          'update, but KeyedModelHandler is wrapping a single '
          'model.')
    # Map cohort ids to a dictionary mapping new model paths to the keys that
    # were originally in that cohort. We will use this to construct our new
    # cohorts.
    # cohort_path_mapping will be structured as follows:
    # {
    # original_cohort_id: {
    #    'update/path/1': ['key1FromOriginalCohort', key2FromOriginalCohort'],
    #    'update/path/2': ['key3FromOriginalCohort', key4FromOriginalCohort'],
    #    }
    # }
    cohort_path_mapping: dict[KeyT, dict[str, list[KeyT]]] = {}
    key_modelid_mapping: dict[KeyT, str] = {}
    seen_keys = set()
    for mp in model_paths:
      keys = mp.keys

View on GitHub (pinned to 12126d8942)