apache/beam · critical

unable to decode bool; expected

Error message

unable to decode bool; expected {0, 1} got %v

What it means

decoder.Bool in the prism runner's engine decodes a single byte as a boolean, accepting only 0 (false) and 1 (true). Any other byte value means the byte stream is corrupted or misaligned relative to the coder that produced it, so the decoder panics rather than silently returning a wrong value.

Solutions

  1. Verify the coder registered for the PCollection actually matches the bytes being produced (re-check the pipeline's coder inference).
  2. Ensure both producer and consumer use the same Beam SDK version so bool coders agree on the {0,1} encoding.
  3. Dump the raw bytes around the failure to find the misalignment point and fix the coder that wrote the extra/mis-sized field.
  4. If using custom coders, emit bools strictly as single bytes 0 or 1.

Example fix

// before (custom coder writing Go bool's textual form)
w.Write([]byte(fmt.Sprintf("%v", value))) // writes "true" -> first byte 't' panics
// after
if value { w.Write([]byte{1}) } else { w.Write([]byte{0}) }
Defensive patterns

Strategy: validation

Validate before calling

// Validate the encoded byte stream before handing it to the runner
func validateBoolBytes(data []byte) error {
    for i, b := range data {
        if b != 0 && b != 1 {
            return fmt.Errorf("byte at %d is %d, not a valid bool encoding", i, b)
        }
    }
    return nil
}

Type guard

func isBoolByte(b byte) bool { return b == 0 || b == 1 }

Prevention

When it happens

Trigger: A encoded data stream (e.g. element or timer blobs from the FnAPI) contains a byte other than 0 or 1 at a position where the coder says a bool should be; typically a decoder misalignment after a previous field was read with the wrong width, or a custom/unknown coder emitting non-standard bool encodings.

Common situations: Mismatched coder versions between SDK language SDKs and prism, a hand-written coder emitting true/false as arbitrary bytes, or byte-stream corruption when elements are passed between runner stages.

Understand the failure class

Related errors


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

Appendix: source

Thrown at sdks/go/pkg/beam/runners/prism/internal/engine/timers.go:239

	b := d.raw[d.cursor:end]
	d.cursor = end
	return b
}

// UnusedBytes returns the remainder of bytes in the buffer that weren't yet used.
// Multiple timers can be provided in a single timers buffer, since multiple dynamic
// timer tags may be set.
func (d *decoder) UnusedBytes() []byte {
	return d.raw[d.cursor:]
}

func (d *decoder) Bool() bool {
	if b := d.Byte(); b == 0 {
		return false
	} else if b == 1 {
		return true
	} else {
		panic(fmt.Sprintf("unable to decode bool; expected {0, 1} got %v", b))
	}
}

func (d *decoder) Pane() typex.PaneInfo {
	first := d.Byte()
	pn := coder.NewPane(first & 0x0f)

	switch first >> 4 {
	case 0:
		// Result encoded in only one pane.
		return pn
	case 1:
		// Result encoded in one pane plus a VarInt encoded integer.
		index := d.Varint()
		pn.Index = index
		if pn.Timing == typex.PaneEarly {
			pn.NonSpeculativeIndex = -1
		} else {

View on GitHub (pinned to 12126d8942)