{"record":{"id":"9a53e8c55de62f50","repo":"apache/beam","slug":"invalid-model-update-sent-many-model-paths-to-update-but","errorCode":null,"errorMessage":"Invalid model update: sent many model paths to update, but KeyedModelHandler is wrapping a single model.","messagePattern":"Invalid model update: sent many model paths to update, but KeyedModelHandler is wrapping a single model\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/ml/inference/base.py","lineNumber":953,"sourceCode":"  def validate_inference_args(self, inference_args: Optional[dict[str, Any]]):\n    if self._single_model:\n      return self._unkeyed.validate_inference_args(inference_args)\n    for mh in self._id_to_mh_map.values():\n      mh.validate_inference_args(inference_args)\n\n  def update_model_paths(\n      self,\n      model: Union[ModelT, _ModelHandlerManager],\n      model_paths: list[KeyModelPathMapping[KeyT]] = None):\n    # When there are many models, the keyed model handler is responsible for\n    # reorganizing the model handlers into cohorts and telling the model\n    # manager to update every cohort's associated model handler. The model\n    # manager is responsible for performing the updates and tracking which\n    # updates have already been applied.\n    if model_paths is None or len(model_paths) == 0 or model is None:\n      return\n    if self._single_model:\n      raise RuntimeError(\n          'Invalid model update: sent many model paths to '\n          'update, but KeyedModelHandler is wrapping a single '\n          'model.')\n    # Map cohort ids to a dictionary mapping new model paths to the keys that\n    # were originally in that cohort. We will use this to construct our new\n    # cohorts.\n    # cohort_path_mapping will be structured as follows:\n    # {\n    # original_cohort_id: {\n    #    'update/path/1': ['key1FromOriginalCohort', key2FromOriginalCohort'],\n    #    'update/path/2': ['key3FromOriginalCohort', key4FromOriginalCohort'],\n    #    }\n    # }\n    cohort_path_mapping: dict[KeyT, dict[str, list[KeyT]]] = {}\n    key_modelid_mapping: dict[KeyT, str] = {}\n    seen_keys = set()\n    for mp in model_paths:\n      keys = mp.keys","sourceCodeStart":935,"sourceCodeEnd":971,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/ml/inference/base.py#L935-L971","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass a single model path (or None) when the KeyedModelHandler wraps one model, matching its single-model mode.","If multi-path updates are intended, wrap multiple handlers: KeyedModelHandler([KeyedModelHandlerTuple(...), ...]).","Guard the update callback: check keyed_mh._single_model (or catch RuntimeError) and route single-model updates accordingly."],"exampleFix":"# before\nkeyed = KeyedModelHandler(MyHandler(path))\nkeyed.update_model_paths(['new_a', 'new_b'])  # RuntimeError\n\n# after\nkeyed = KeyedModelHandler([\n  KeyedModelHandlerTuple(MyHandler(path), ['cohort_a']),\n  KeyedModelHandlerTuple(MyHandler(path2), ['cohort_b'])])\nkeyed.update_model_paths(['new_a', 'new_b'])","handlingStrategy":"validation","validationCode":"if getattr(keyed, '_single_model', True) and model_paths and len(model_paths) > 1:\n    raise ValueError('Single-model KeyedModelHandler cannot accept multiple update paths')","typeGuard":"def supports_multi_path_update(keyed):\n    return not getattr(keyed, '_single_model', True)","tryCatchPattern":"try:\n    keyed.update_model_paths(model_paths)\nexcept RuntimeError as e:\n    if 'wrapping a single model' in str(e):\n        keyed.update_model_paths([model_paths[0]])\n    else:\n        raise","preventionTips":["Match the update-path arity to the handler mode: one path for single, many for multi-handler.","Guard background model-refresh callbacks with a handler-mode check.","Document whether a pipeline's handler is keyed-single or keyed-multi before enabling auto refresh."],"tags":["python","apache-beam","ml-inference","model-update","invalid-state"],"backgroundTag":"invalid-state-transition","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}