{"record":{"id":"36b315a760f03516","repo":"kubernetes/kops","slug":"unable-to-find-containerd-version","errorCode":null,"errorMessage":"unable to find containerd version","messagePattern":"unable to find containerd version","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/nodemodel/wellknownassets/containerd.go","lineNumber":60,"sourceCode":"\n\tcanonicalURL := \"\"\n\tknownHash := \"\"\n\n\tif containerd.Packages != nil {\n\t\tif arch == architectures.ArchitectureAmd64 && containerd.Packages.UrlAmd64 != nil && containerd.Packages.HashAmd64 != nil {\n\t\t\tcanonicalURL = fi.ValueOf(containerd.Packages.UrlAmd64)\n\t\t\tknownHash = fi.ValueOf(containerd.Packages.HashAmd64)\n\t\t}\n\t\tif arch == architectures.ArchitectureArm64 && containerd.Packages.UrlArm64 != nil && containerd.Packages.HashArm64 != nil {\n\t\t\tcanonicalURL = fi.ValueOf(containerd.Packages.UrlArm64)\n\t\t\tknownHash = fi.ValueOf(containerd.Packages.HashArm64)\n\t\t}\n\t}\n\n\tif canonicalURL == \"\" {\n\t\tversion := fi.ValueOf(containerd.Version)\n\t\tif version == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"unable to find containerd version\")\n\t\t}\n\n\t\tassetURL, err := findContainerdVersionUrl(arch, version)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcanonicalURL = assetURL.String()\n\t}\n\n\treturn buildFileAsset(assetBuilder, canonicalURL, knownHash)\n}\n\nfunc findContainerdVersionUrl(arch architectures.Architecture, version string) (*url.URL, error) {\n\tsv, err := semver.ParseTolerant(version)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to parse version string: %q\", version)\n\t}\n\tif sv.LT(semver.MustParse(\"2.1.0\")) {","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/nodemodel/wellknownassets/containerd.go#L42-L78","documentation":"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.","triggerScenarios":"FindContainerdAsset is called where containerd.Packages is nil (canonicalURL stayed empty) and containerd.Version is empty/unset.","commonSituations":"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.","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"],"exampleFix":"// before\ncontainerd:\n  version: \"\"\n// after\ncontainerd:\n  version: \"2.1.0\"","handlingStrategy":"validation","validationCode":"c := ig.RawClusterSpec().Containerd\nif c != nil && c.Packages == nil && fi.ValueOf(c.Version) == \"\" {\n    return fmt.Errorf(\"containerd.version must be set when containerd.packages is not used\")\n}","typeGuard":null,"tryCatchPattern":"asset, err := wellknownassets.FindContainerdAsset(ig, assetBuilder, arch)\nif err != nil && strings.Contains(err.Error(), \"unable to find containerd version\") {\n    return fmt.Errorf(\"set containerd.version in the cluster spec: %w\", err)\n}","preventionTips":["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"],"tags":["containerd","missing-version","cluster-spec"],"backgroundTag":"missing-config-version","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}