apache/beam · error

Node type not bound

Error message

Node type not bound: %v

What it means

Graph.NewNode panics when the supplied typex.FullType is not bound, i.e. its type has unresolved or missing type information (typex.IsBound fails). Beam graph nodes require fully resolved concrete types to plan encoding and windowing.

Solutions

  1. Pass a concrete typex.FullType, e.g. typex.New(reflect.TypeOf(Foo{})), not a variable type
  2. Resolve any typex.T variables via substitution before node creation
  3. Log/print the type before NewNode to identify the unbound component

Example fix

// before
t := typex.NewVariable("T")
n := g.NewNode(t, w, true) // unbound
// after
t := typex.New(reflect.TypeOf(Foo{}))
n := g.NewNode(t, w, true)
Defensive patterns

Strategy: validation

Validate before calling

if !typex.IsBound(t) {
	return fmt.Errorf("type %v is not bound; resolve to a concrete type before NewNode", t)
}

Type guard

func isConcreteFullType(t typex.FullType) bool { return typex.IsBound(t) }

Try / catch

func safeNewNode(g *graph.Graph, t typex.FullType, w *window.WindowingStrategy, bounded bool) (n *graph.Node, err error) {
	defer func() { if r := recover(); r != nil { err = fmt.Errorf("newNode: %v", r) } }()
	return g.NewNode(t, w, bounded), nil
}

Prevention

When it happens

Trigger: Calling graph.NewNode(t, w, bounded) with an unbound type such as a type with typex.NewVariable type variables, an unresolved generic, or a zero-value FullType built from an invalid reflect.Type.

Common situations: Constructing graph nodes in custom transforms or tests using typex.New/Variable incorrectly; reflection-based code that failed to resolve a concrete type; misuse of typex APIs introduced while hand-building pipeline graphs.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at sdks/go/pkg/beam/core/graph/graph.go:74

	g.scopes = append(g.scopes, s)
	return s
}

// NewEdge creates a new edge of the graph in the supplied scope.
func (g *Graph) NewEdge(parent *Scope) *MultiEdge {
	if parent == nil {
		panic("Scope is nil")
	}
	id := len(g.edges) + 1
	e := &MultiEdge{id: id, parent: parent}
	g.edges = append(g.edges, e)
	return e
}

// NewNode creates a new node in the graph of the supplied fulltype.
func (g *Graph) NewNode(t typex.FullType, w *window.WindowingStrategy, bounded bool) *Node {
	if !typex.IsBound(t) {
		panic(fmt.Sprintf("Node type not bound: %v", t))
	}
	id := len(g.nodes) + 1
	n := &Node{id: id, t: t, w: w, bounded: bounded}
	g.nodes = append(g.nodes, n)
	return n
}

// Build performs finalization on the graph. It verifies the correctness of the
// graph structure, typechecks the plan and returns a slice of the edges in
// the graph.
func (g *Graph) Build() ([]*MultiEdge, []*Node, error) {
	// Build a map of all nodes listed in g.nodes.
	nodes := make(map[*Node]bool)
	for _, n := range g.nodes {
		nodes[n] = true
		if n.Coder == nil {
			return nil, nil, errors.Errorf("node %v in graph has undefined coder", n.id)
		}

View on GitHub (pinned to 12126d8942)