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
- Set containerd.version in the cluster spec to a supported release (>= 2.1.0), e.g. 2.1.0
- If using containerd.packages, ensure it yields a non-empty canonicalURL so the version is not needed
- 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
- Set containerd.version explicitly in every cluster spec template
- Treat an empty version string as a config error in pre-apply validation
- When using packages overrides, assert they produce a canonical URL
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
- unable to find containerd config
- validation of the full cluster and instance group specs fail
- error populating configuration: %v
- error loading Cluster %q: %v
- error parsing Cluster %q: %v
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/36b315a760f03516.
Report an issue: GitHub.