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
- Align the Go SDK version used by the worker with the one that built the pipeline (use --workerHarnessContainerImage or matching releases)
- Inspect the opcode value in the message and find which transform produced it in the graph dump
- Remove/handle hand-constructed plan nodes; build pipelines only through the beam package APIs
- 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
- Match worker and pipeline-builder SDK versions exactly
- Build plans only through the beam/graph public APIs
- Dump the plan when debugging custom runners and check opcodes before link building
- Pin worker container images in CI to the SDK release used for submission
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
- AfterProcessingTime trigger set without a delay or…
- At least one subtrigger required for composite triggers.
- attempted to add namespace to missing coder id
- attempted to add namespace to missing windowing strategy id
- batch: failed to marshal worker UUID
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)