apache/beam · error

bad pair: %+v

Error message

bad pair: %+v

What it means

DecodeCoderRef converts an encoded CoderRef back into a usable coder. A pair-type ref must have exactly two components (key and value); any other count means the encoded form is corrupt or was produced by an incompatible encoder, so decoding fails with the full ref in the message.

Solutions

  1. Inspect the printed CoderRef and confirm the pipeline graph source; regenerate it from the original pipeline rather than reusing a stale artifact.
  2. Ensure the SDK version decoding the pipeline matches the version that encoded it.
  3. If using custom graph-rewriting tools, verify they preserve pair-coder component count.
  4. Re-export/rebuild the pipeline so the coder graph is re-encoded from valid in-memory coders.

Example fix

// before (corrupt ref)
ref := &graphx.CoderRef{Type: graphx.Pair, Components: []*graphx.CoderRef{key}}
c, err := graphx.DecodeCoderRef(ref)

// after
ref := &graphx.CoderRef{Type: graphx.Pair, Components: []*graphx.CoderRef{key, value}}
c, err := graphx.DecodeCoderRef(ref)
Defensive patterns

Strategy: try-catch

Validate before calling

func validPairRef(r *graphx.CoderRef) bool {
	return r != nil && r.Type == graphx.Pair && len(r.Components) == 2
}

Type guard

func isPairRef(r *graphx.CoderRef) bool { return r != nil && r.Type == graphx.Pair && len(r.Components) == 2 }

Try / catch

c, err := graphx.DecodeCoderRef(ref)
if err != nil {
	if strings.Contains(err.Error(), "bad pair") {
		return nil, fmt.Errorf("corrupt pair coder ref %v: regenerate the pipeline artifact: %w", ref, err)
	}
	return nil, err
}

Prevention

When it happens

Trigger: Decoding a CoderRef with Type pairType whose Components slice has length != 2 — e.g. during pipeline deserialization of a model/Job message whose coder graph was corrupted, truncated, or produced by a different Beam version.

Common situations: Loading a pipeline artifact or model-pipeline.proto edited by hand; cross-version decode (SDK encodes pair differently then another version decodes); corrupted service responses for the Dataflow pipeline graph; custom tooling that rewrites CoderRef components.

Related errors


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

Appendix: source

Thrown at sdks/go/pkg/beam/core/runtime/graphx/dataflow.go:252

	switch c.Type {
	case bytesType:
		return coder.NewBytes(), nil

	case boolType:
		return coder.NewBool(), nil

	case varIntType:
		return coder.NewVarInt(), nil

	case doubleType:
		return coder.NewDouble(), nil

	case stringType:
		return coder.NewString(), nil

	case pairType:
		if len(c.Components) != 2 {
			return nil, errors.Errorf("bad pair: %+v", c)
		}

		key, err := DecodeCoderRef(c.Components[0])
		if err != nil {
			return nil, err
		}

		elm := c.Components[1]
		if elm.Type == streamType {
			// TODO(https://github.com/apache/beam/issues/18032): If CoGBK with > 1 input, handle as special GBK. We expect
			// it to be encoded as CoGBK<K,LP<Union<V,W,..>>. Remove this handling once
			// CoGBK has a first-class representation.

			if refs, ok := isCoGBKList(elm.Components[0]); ok {
				values, err := DecodeCoderRefs(refs)
				if err != nil {
					return nil, err
				}

View on GitHub (pinned to 12126d8942)