{"record":{"id":"fc03c36a5c88b7b1","repo":"apache/beam","slug":"cannot-insert-different-protos-r-and-r-with-the-same-id-r","errorCode":null,"errorMessage":"Cannot insert different protos %r and %r with the same ID %r","messagePattern":"Cannot insert different protos %r and %r with the same ID %r","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/runners/pipeline_context.py","lineNumber":146,"sourceCode":"            obj=obj, obj_type=self._obj_type, label=label),\n        maybe_new_proto)\n\n  def get_id_to_proto_map(self) -> dict[str, message.Message]:\n    return self._id_to_proto\n\n  def get_proto_from_id(self, id: str) -> message.Message:\n    return self.get_id_to_proto_map()[id]\n\n  def put_proto(\n      self,\n      id: str,\n      proto: message.Message,\n      ignore_duplicates: bool = False) -> str:\n    if not ignore_duplicates and id in self._id_to_proto:\n      raise ValueError(\"Id '%s' is already taken.\" % id)\n    elif (ignore_duplicates and id in self._id_to_proto and\n          self._id_to_proto[id] != proto):\n      raise ValueError(\n          'Cannot insert different protos %r and %r with the same ID %r',\n          self._id_to_proto[id],\n          proto,\n          id)\n    self._id_to_proto[id] = proto\n    return id\n\n  def __getitem__(self, id: str) -> Any:\n    return self.get_by_id(id)\n\n  def __contains__(self, id: str) -> bool:\n    return id in self._id_to_proto\n\n\nclass PipelineContext(object):\n  \"\"\"For internal use only; no backwards-compatibility guarantees.\n\n  Used for accessing and constructing the referenced objects of a Pipeline.","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/runners/pipeline_context.py#L128-L164","documentation":"Raised by PipelineContext.put_proto when two different proto objects are registered under the same ID in the pipeline context while ignore_duplicates=True. The context maps component IDs (PCollection, Transform, Coder proto IDs) to their proto messages; silently overwriting a differing proto would corrupt the pipeline model, so the mismatch is rejected. It is an internal consistency guard for the Beam pipeline proto builder.","triggerScenarios":"Calling get_by_proto (or any code path that calls put_proto with ignore_duplicates=True) when an ID string is reused for a semantically different proto message — e.g. two distinct pcollections/transforms generated the same ID string, or a proto object's id() key hashing collided/reused after object reuse.","commonSituations":"Custom pipeline construction or runner code that manually assigns component IDs; reusing a PipelineContext across pipelines; frameworks generating PCollection IDs from non-unique labels; errors where a None/uniqueness check was skipped before generating IDs.","solutions":["Ensure every proto registered gets a unique ID (use context' id-generating helpers such as ptransform.label uniqueness or context.put_proto with fresh IDs)","Check for reuse of PipelineContext objects or proto messages across pipelines; create a fresh context per pipeline","If a duplicate proto is truly identical, allow it: pass ignore_duplicates=True only where protos are equal, or reuse the existing ID from get_by_proto","Update Beam version — older versions had ID-generation collisions; regenerate your pipeline with a current SDK"],"exampleFix":"// before\nctx = PipelineContext(default_environment=None)\nid = ctx.put_proto(same_id, proto_a, ignore_duplicates=True)\nid = ctx.put_proto(same_id, proto_b, ignore_duplicates=True)  # raises\n// after\nid_a = ctx.put_proto(proto_a)\nid_b = ctx.put_proto(proto_b)  # let the context mint distinct IDs\nassert id_a != id_b","handlingStrategy":"try-catch","validationCode":"existing = ctx._id_to_proto.get(id)\nif existing is not None and existing != proto:\n    raise ValueError(f'ID {id!r} already bound to a different proto')","typeGuard":"def is_id_free(ctx, id: str) -> bool:\n    return id not in ctx._id_to_proto","tryCatchPattern":"try:\n    ctx.put_proto(proto, ignore_duplicates=True)\nexcept ValueError as e:\n    if 'Cannot insert different protos' in str(e):\n        existing = ctx.get_by_id(id)\n        id = ctx.put_proto(proto)  # mint a fresh unique ID\n    else:\n        raise","preventionTips":["Never assign component ID strings manually; let PipelineContext generate them","Use get_by_proto to reuse the existing ID for an identical proto instead of re-inserting","Create a fresh PipelineContext per pipeline; never share one across pipelines","Hash-compare protos before reuse to confirm they are truly equal"],"tags":["python","apache-beam","pipeline-construction","duplicate-id"],"backgroundTag":"internal-invariant-violation","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}