k3s-io/k3s · warning

invalid GVK format: %s

Error message

invalid GVK format: %s

What it means

getGVK parses strings produced by schema.GroupVersionKind.String(), i.e. exactly 'group/version, Kind=Kind' ('apps/v1, Kind=Deployment'). It splits on the literal ', Kind=' and requires exactly one separator; anything else is rejected. It is used to parse the GVK annotation k3s stores on auto-deployed manifest Addons. Note: both call sites (pkg/deploy/controller.go:256, 303) discard this error via `if gvk, err := getGVK(...); err == nil`, so in shipped k3s the error never propagates - it only fires for direct callers (e.g. tests).

Source

Thrown at pkg/deploy/controller.go:534

		subPath := filepath.Join(namePath[0:i]...)
		if disables[subPath] {
			return true
		}
	}
	if !util.HasSuffixI(fileName, ".yaml", ".yml", ".json") {
		return false
	}
	// Check the basename against the disables map
	baseFile := filepath.Base(fileName)
	suffix := filepath.Ext(baseFile)
	baseName := strings.TrimSuffix(baseFile, suffix)
	return disables[baseName]
}

func getGVK(s string) (*schema.GroupVersionKind, error) {
	parts := strings.Split(s, ", Kind=")
	if len(parts) != 2 {
		return nil, fmt.Errorf("invalid GVK format: %s", s)
	}
	gvk := &schema.GroupVersionKind{}
	gvk.Group, gvk.Version = kv.Split(parts[0], "/")
	gvk.Kind = parts[1]
	return gvk, nil
}

func getGVKString(gvks []schema.GroupVersionKind) string {
	strs := make([]string, len(gvks))
	for i, gvk := range gvks {
		strs[i] = gvk.String()
	}
	return strings.Join(strs, gvkSep)
}

View on GitHub (pinned to 6ba341e396)

Solutions

  1. Keep GVK strings in the canonical GroupVersionKind.String() format: 'group/version, Kind=Kind'.
  2. Let k3s manage the GVK annotation itself - it is written by getGVKString after each successful apply; delete a corrupt annotation and let the controller rewrite it.
  3. If calling getGVK from code, validate/normalize the input first instead of relying on the error.

Example fix

// before
addon.Annotations["deploy.k3s.cattle.io/gvks"] = "apps/v1 Deployment"

// after
addon.Annotations["deploy.k3s.cattle.io/gvks"] = "apps/v1, Kind=Deployment"
Defensive patterns

Strategy: type-guard

Type guard

// Narrow a string to a valid GroupVersionKind String() before parsing:
func isGVKString(s string) bool {
    parts := strings.Split(s, ", Kind=")
    if len(parts) != 2 { return false }
    seg := strings.Split(parts[0], "/")
    if len(seg) < 1 || len(seg) > 2 { return false } // 'v1' or 'group/version'
    return parts[0] != "" && parts[1] != ""
}

if isGVKString(s) { gvk, _ := getGVK(s) }

Try / catch

// Follow the controller's own pattern - skip invalid entries instead of failing:
if gvk, err := getGVK(gvkString); err == nil {
    addonGVKs = append(addonGVKs, *gvk)
} else {
    log.Warnf("ignoring malformed GVK %q: %v", gvkString, err)
}

Prevention

When it happens

Trigger: Invoking getGVK with a string lacking the ', Kind=' separator or containing it more than once, e.g. 'apps/v1', 'Deployment', or 'apps/v1, Kind=Deployment, Kind=Pod' (pkg/deploy/controller.go:531-538). In-cluster, only a hand-edited GVK annotation on an Addon object would carry such a value.

Common situations: Manually editing the auto-deployed Addon annotations (kubectl annotate addons ...); a script writing GVK strings in a different format; unit tests feeding unstructured GVK strings.

Related errors


AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15). Data as JSON: /api/errors/157906adc242868a. Report an issue: GitHub.