{"record":{"id":"f711f2f2a3a018ab","repo":"apache/beam","slug":"output-value-coder-s-for-transform-s-must-be-an-iterable","errorCode":null,"errorMessage":"Output value coder %s for transform %s must be an iterable coder, but uses URN %s","messagePattern":"Output value coder (.+?) for transform (.+?) must be an iterable coder, but uses URN (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/runners/pipeline_utils.py","lineNumber":107,"sourceCode":"      output_coder = get_coder(next(iter(transform_proto.outputs.values())))\n      if input_coder.spec.urn != common_urns.coders.KV.urn:\n        raise ValueError(\n            \"Bad coder for input of %s: %s\" % (transform_id, input_coder))\n      if output_coder.spec.urn != common_urns.coders.KV.urn:\n        raise ValueError(\n            \"Bad coder for output of %s: %s\" % (transform_id, output_coder))\n      input_key_coder_id = input_coder.component_coder_ids[0]\n      output_key_coder_id = output_coder.component_coder_ids[0]\n      if input_key_coder_id != output_key_coder_id:\n        raise ValueError(\n            \"Input key coder %s does not match output key coder %s for \"\n            \"transform %s\" %\n            (input_key_coder_id, output_key_coder_id, transform_id))\n      output_values_coder_id = output_coder.component_coder_ids[1]\n      output_values_coder = pipeline_proto.components.coders[\n          output_values_coder_id]\n      if output_values_coder.spec.urn != common_urns.coders.ITERABLE.urn:\n        raise ValueError(\n            \"Output value coder %s for transform %s must be an iterable \"\n            \"coder, but uses URN %s\" % (\n                output_values_coder_id,\n                transform_id,\n                output_values_coder.spec.urn))\n      input_value_coder_id = input_coder.component_coder_ids[1]\n      output_value_coder_id = output_values_coder.component_coder_ids[0]\n      if output_value_coder_id != input_value_coder_id:\n        raise ValueError(\n            \"Input value coder %s does not match output value coder %s for \"\n            \"transform %s\" %\n            (input_value_coder_id, output_value_coder_id, transform_id))\n    elif transform_proto.spec.urn == common_urns.primitives.ASSIGN_WINDOWS.urn:\n      if not transform_proto.inputs:\n        raise ValueError(\"Missing input for transform: %s\" % transform_proto)\n    elif transform_proto.spec.urn == common_urns.primitives.PAR_DO.urn:\n      if not transform_proto.inputs:\n        raise ValueError(\"Missing input for transform: %s\" % transform_proto)","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/runners/pipeline_utils.py#L89-L125","documentation":"Raised by validate_transform: the value component of a GROUP_BY_KEY output KV coder must be an ITERABLE coder (GBK yields KV<K, Iterable<V>>), but the proto references a coder with a different URN. This keeps the post-GBK encoding contract intact for runners that rely on iterable streaming semantics.","triggerScenarios":"output_values_coder.spec.urn != common_urns.coders.ITERABLE.urn for the GBK output KV's second component — e.g. the output coder was built with a plain list/sequence coder or an unrelated coder ID.","commonSituations":"Custom coder construction when rewriting GBK; hand-built protos; cross-language graph conversion assigning the wrong value coder component.","solutions":["Wrap the value component in an iterable coder (beam:coders:iterable:v1) whose element coder is the input value coder","Rebuild the pipeline in Beam Python and let TypeHints infer KV[K, Iterable[V]] coders","Fix custom graph-rewrite code so GBK output coders are always KV(key_coder, iterable(value_coder))","Re-serialize with a current SDK"],"exampleFix":"// before\nout_kv = make_kv_coder(key_coder_id, value_coder_id)  # plain value coder\n// after\niter_coder = make_iterable_coder(value_coder_id)\nout_kv = make_kv_coder(key_coder_id, iter_coder)","handlingStrategy":"validation","validationCode":"def check_value_coder_is_iterable(pipeline, t) -> bool:\n    if t.spec.urn != common_urns.primitives.GROUP_BY_KEY.urn:\n        return True\n    out_c = get_coder(next(iter(t.outputs.values())))\n    values_coder = pipeline.components.coders[out_c.component_coder_ids[1]]\n    return values_coder.spec.urn == common_urns.coders.ITERABLE.urn","typeGuard":"def gbk_values_iterable(pipeline, t) -> bool:\n    out_c = gbk_output_coder(pipeline, t)\n    return pipeline.components.coders[out_c.component_coder_ids[1]].spec.urn \\\n        == 'beam:coders:iterable:v1'","tryCatchPattern":"try:\n    validate_pipeline_graph(pipeline_proto)\nexcept ValueError as e:\n    if 'must be an iterable coder' in str(e):\n        wrap_in_iterable_coder(pipeline_proto)\n    else:\n        raise","preventionTips":["Always encode GBK output values as KV[K, Iterable[V]] (iterable coder component)","Never replace the GBK output value coder with a list/sequence coder","Add a proto-level coder-shape assertion to custom translator tests","Keep pipelines generated by current SDK versions"],"tags":["python","apache-beam","pipeline-validation","coders"],"backgroundTag":"schema-validation-failed","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"}