apache/beam · error

Opcode should be one of ParDo or Combine, but it is

Error message

Opcode should be one of ParDo or Combine, but it is: %v

What it means

makeLink in the exec translator maps graph nodes to executable units. It only knows how to lower nodes whose opcode is ParDo or Combine; any other opcode reaching makeLink violates the translator's expectations and panics with this message. This is an internal translation invariant — such nodes should have been handled earlier in translation.

Solutions

  1. Align the Go SDK version used by the worker with the one that built the pipeline (use --workerHarnessContainerImage or matching releases)
  2. Inspect the opcode value in the message and find which transform produced it in the graph dump
  3. Remove/handle hand-constructed plan nodes; build pipelines only through the beam package APIs
  4. If all versions match and a normal pipeline triggers it, file a Beam bug with the pipeline proto
Defensive patterns

Strategy: validation

Validate before calling

if op != graph.OpcodeParDo && op != graph.OpcodeCombine {
    return fmt.Errorf("unsupported opcode for link: %v", op)
}

Prevention

When it happens

Trigger: A plan/pipe element with an unexpected opcode (e.g. Impulse, Flatten, or a runner-specific opcode) reaching makeLink; corrupted or hand-crafted pipeline plans; SDK/runner version skew producing opcodes this translator doesn't recognize.

Common situations: Running pipelines through custom runners or external environments that emit raw plans; mixing SDK versions (worker older than frontend); hand-built graphs in tests bypassing the graph package's validation.

Related errors


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

Appendix: source

Thrown at sdks/go/pkg/beam/core/runtime/exec/translate.go:700

					ma := &MergeAccumulators{Combine: cn}
					if pc, ok := ma.Out.(*PCollection); ok {
						if eo, ok := pc.Out.(*ExtractOutput); ok {
							// Strip PCollections from between MergeAccumulators and ExtractOutputs
							// as it's a synthetic PCollection.
							b.units = b.units[:len(b.units)-1]
							ma.Out = eo
						}
					}
					u = ma
				case urnPerKeyCombineExtract:
					u = &ExtractOutput{Combine: cn}
				case urnPerKeyCombineConvert:
					u = &ConvertToAccumulators{Combine: cn}
				default: // For unlifted combines
					u = cn
				}
			default:
				panic(fmt.Sprintf("Opcode should be one of ParDo or Combine, but it is: %v", op))
			}

		case graphx.URNIterableSideInputKey:
			u = &FixedKey{UID: b.idgen.New(), Key: []byte(iterableSideInputKey), Out: out[0]}

		case graphx.URNInject:
			c, _, err := b.makeCoderForPCollection(from)
			if err != nil {
				return nil, err
			}
			if !coder.IsKV(c) {
				return nil, errors.Errorf("unexpected inject coder: %v", c)
			}
			valCoder := c.Components[1]
			// JIRA BEAM-12438 - an extra LP coder can get added here, but isn't added
			// on decode. Strip them until we get a better fix.
			if valCoder.Kind == coder.LP {
				// strip unexpected length prefix coder.

View on GitHub (pinned to 12126d8942)