cilium/cilium · error

failed to decode non-Unstructured object

Error message

failed to decode non-Unstructured object

What it means

After confirming the object claims Table kind, unstructuredToTable requires the concrete Go type to be *unstructured.Unstructured so its raw map can be converted into metav1.Table. If the object is some other runtime.Object implementation, this error is returned. It signals a decoding/typing mismatch in the caller's pipeline.

Source

Thrown at cilium-cli/k8s/tables.go:20

// Copyright Authors of Cilium

package k8s

import (
	"fmt"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
)

func unstructuredToTable(o runtime.Object) (*metav1.Table, error) {
	if metav1.SchemeGroupVersion.WithKind("Table") != o.GetObjectKind().GroupVersionKind() {
		return nil, fmt.Errorf("failed to decode non-Table object")
	}
	u, ok := o.(*unstructured.Unstructured)
	if !ok {
		return nil, fmt.Errorf("failed to decode non-Unstructured object")
	}
	t := &metav1.Table{}
	if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, t); err != nil {
		return nil, err
	}
	for i := range t.Rows {
		row := &t.Rows[i]
		if row.Object.Raw == nil || row.Object.Object != nil {
			continue
		}
		converted, err := runtime.Decode(unstructured.UnstructuredJSONScheme, row.Object.Raw)
		if err != nil {
			return nil, err
		}
		row.Object.Object = converted
	}
	return t, nil
}

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Decode the response into unstructured.Unstructured (e.g. via runtime.DecodeInto with an unstructured decoder)
  2. Ensure the list request goes through the RESTClient returning unstructured data, not the typed clientset
  3. Convert the typed object to unstructured via runtime.DefaultUnstructuredConverter.ToUnstructured before passing

Example fix

// before
pods, _ := clientset.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{})
table, err := unstructuredSliceToTable([]runtime.Object{pods})
// after
u := &unstructured.Unstructured{}
untime.DefaultUnstructuredConverter.ToUnstructured(pods, &u.Object)
table, err := unstructuredSliceToTable([]runtime.Object{u})
Defensive patterns

Strategy: type-guard

Validate before calling

if _, ok := obj.(*unstructured.Unstructured); !ok {
	return errors.New("object must be *unstructured.Unstructured for table conversion")
}

Type guard

func isUnstructured(o runtime.Object) (*unstructured.Unstructured, bool) {
	u, ok := o.(*unstructured.Unstructured)
	return u, ok
}

Try / catch

table, err := unstructuredToTable(o)
if err != nil && strings.Contains(err.Error(), "non-Unstructured") {
	u := &unstructured.Unstructured{}
	runtime.DefaultUnstructuredConverter.ToUnstructured(o, &u.Object)
	table, err = unstructuredToTable(u)
}

Prevention

When it happens

Trigger: Calling unstructuredToTable/unstructuredSliceToTable with an object that passed the Table GVK check but is a typed struct (e.g. *corev1.PodList) or a custom runtime.Object rather than *unstructured.Unstructured.

Common situations: Mixing typed clientset results into the unstructured table pipeline; custom decoders producing typed objects; refactoring that changed the decode path.

Understand the failure class

Related errors


AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31). Data as JSON: /api/errors/b4012ae431356fd9. Report an issue: GitHub.