kubernetes/kops · error

unable to find containerd version

Error message

unable to find containerd version

What it means

When no Packages/SkipVersion URL override provides a canonical URL, FindContainerdAsset falls back to building a release URL from containerd.Version. If that version string is empty, no asset can be located, so it returns this error. It ensures containerd assets always have an explicit version to build the download URL from.

Source

Thrown at pkg/nodemodel/wellknownassets/containerd.go:60

	canonicalURL := ""
	knownHash := ""

	if containerd.Packages != nil {
		if arch == architectures.ArchitectureAmd64 && containerd.Packages.UrlAmd64 != nil && containerd.Packages.HashAmd64 != nil {
			canonicalURL = fi.ValueOf(containerd.Packages.UrlAmd64)
			knownHash = fi.ValueOf(containerd.Packages.HashAmd64)
		}
		if arch == architectures.ArchitectureArm64 && containerd.Packages.UrlArm64 != nil && containerd.Packages.HashArm64 != nil {
			canonicalURL = fi.ValueOf(containerd.Packages.UrlArm64)
			knownHash = fi.ValueOf(containerd.Packages.HashArm64)
		}
	}

	if canonicalURL == "" {
		version := fi.ValueOf(containerd.Version)
		if version == "" {
			return nil, fmt.Errorf("unable to find containerd version")
		}

		assetURL, err := findContainerdVersionUrl(arch, version)
		if err != nil {
			return nil, err
		}
		canonicalURL = assetURL.String()
	}

	return buildFileAsset(assetBuilder, canonicalURL, knownHash)
}

func findContainerdVersionUrl(arch architectures.Architecture, version string) (*url.URL, error) {
	sv, err := semver.ParseTolerant(version)
	if err != nil {
		return nil, fmt.Errorf("unable to parse version string: %q", version)
	}
	if sv.LT(semver.MustParse("2.1.0")) {

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Set containerd.version in the cluster spec to a supported release (>= 2.1.0), e.g. 2.1.0
  2. If using containerd.packages, ensure it yields a non-empty canonicalURL so the version is not needed
  3. Fix test/programmatic fixtures to populate Containerd.Version

Example fix

// before
containerd:
  version: ""
// after
containerd:
  version: "2.1.0"
Defensive patterns

Strategy: validation

Validate before calling

c := ig.RawClusterSpec().Containerd
if c != nil && c.Packages == nil && fi.ValueOf(c.Version) == "" {
    return fmt.Errorf("containerd.version must be set when containerd.packages is not used")
}

Try / catch

asset, err := wellknownassets.FindContainerdAsset(ig, assetBuilder, arch)
if err != nil && strings.Contains(err.Error(), "unable to find containerd version") {
    return fmt.Errorf("set containerd.version in the cluster spec: %w", err)
}

Prevention

When it happens

Trigger: FindContainerdAsset is called where containerd.Packages is nil (canonicalURL stayed empty) and containerd.Version is empty/unset.

Common situations: Cluster spec sets a containerd block but omits the version field; YAML with `version: ""`; test fixtures that create the config without a version; upgrades where the version field was removed by tooling.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/36b315a760f03516. Report an issue: GitHub.