apache/beam · error

structx: InferFieldNames of non-struct type

Error message

structx: InferFieldNames of non-struct type %s

What it means

structx.InferFieldNames reflects over a struct type to derive field names from tags. It documents that it panics when the passed reflect.Type's Kind is not reflect.Struct. Callers such as constructSelectStatement, inferProjection, and Read rely on this precondition, so passing any non-struct type immediately aborts.

Solutions

  1. Dereference pointer types before calling: use t.Elem() when t.Kind() == reflect.Ptr.
  2. Pass the struct's value type, not a slice/map containing it (use the element type of a slice).
  3. Guard the call with t.Kind() != reflect.Struct before invoking InferFieldNames.
  4. Ensure your data model is a plain struct with exported fields when using structx-based readers.

Example fix

// before
names := structx.InferFieldNames(reflect.TypeOf(&Row{}), "beam")
// after
t := reflect.TypeOf(&Row{})
for t != nil && t.Kind() == reflect.Ptr { t = t.Elem() }
names := structx.InferFieldNames(t, "beam")
Defensive patterns

Strategy: type-guard

Validate before calling

t := reflect.TypeOf(v)
for t != nil && t.Kind() == reflect.Ptr { t = t.Elem() }
if t == nil || t.Kind() != reflect.Struct {
    return fmt.Errorf("structx needs a struct type, got %v", t)
}

Type guard

func isStructType(t reflect.Type) bool {
    for t != nil && t.Kind() == reflect.Ptr { t = t.Elem() }
    return t != nil && t.Kind() == reflect.Struct
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        if s, ok := r.(string); ok && strings.Contains(s, "InferFieldNames of non-struct type") {
            log.Fatalf("structx misuse: %s", s)
        }
        panic(r)
    }
}()

Prevention

When it happens

Trigger: Passing a reflect.Type whose Kind is Slice, Map, Ptr, Int, etc. to InferFieldNames — e.g. calling it on reflect.TypeOf([]Row{}) or on a pointer type instead of the struct itself.

Common situations: Reading BigQuery rows / building projections with structx and accidentally passing reflect.TypeOf(&MyStruct{}) (pointer) or the element type of a slice; generic helpers that forward arbitrary types.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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

Appendix: source

Thrown at sdks/go/pkg/beam/util/structx/struct.go:30

// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package structx provides utilities for working with structs.
package structx

import (
	"fmt"
	"reflect"
	"strings"
)

// InferFieldNames returns the field names of the given struct type and tag key. Includes only
// exported fields. If a field's tag key is empty or not set, uses the field's name. If a field's
// tag key is set to '-', omits the field. Panics if the type's kind is not a struct.
func InferFieldNames(t reflect.Type, key string) []string {
	if t.Kind() != reflect.Struct {
		panic(fmt.Sprintf("structx: InferFieldNames of non-struct type %s", t))
	}

	var names []string

	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)

		if field.Anonymous {
			names = append(names, InferFieldNames(field.Type, key)...)
			continue
		}

		if !field.IsExported() {
			continue
		}

		value := field.Tag.Get(key)
		name := strings.Split(value, ",")[0]

View on GitHub (pinned to 12126d8942)