kubernetes/kops · error

unable to parse version string: %q

Error message

unable to parse version string: %q

What it means

findContainerdVersionUrl parses the containerd version with semver.ParseTolerant before constructing the GitHub release URL. If the string is not a tolerable semantic version (missing numbers, garbage characters), the parse fails and this error is returned wrapping the raw version. It blocks building a release URL from an unusable version.

Source

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

		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")) {
		return nil, fmt.Errorf("unsupported legacy containerd version: %q", version)
	}

	var u string
	switch arch {
	case architectures.ArchitectureAmd64:
		u = fmt.Sprintf(containerdReleaseUrlAmd64, version, version)
	case architectures.ArchitectureArm64:
		u = fmt.Sprintf(containerdReleaseUrlArm64, version, version)
	default:
		return nil, fmt.Errorf("unknown arch: %q", arch)
	}

	return url.Parse(u)
}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Set containerd.version to a clean semver value such as '2.1.0' (the 'v' prefix is tolerated)
  2. Strip any filename/suffix from the configured version so only 'X.Y.Z' remains
  3. Pin to a known containerd release listed on the containerd GitHub releases page

Example fix

// before
containerd:
  version: containerd-2.1.0-linux-amd64
// after
containerd:
  version: "2.1.0"
Defensive patterns

Strategy: validation

Validate before calling

if _, err := semver.ParseTolerant(containerdVersion); err != nil {
    return fmt.Errorf("containerd.version %q is not a valid semver: %v", containerdVersion, err)
}

Try / catch

asset, err := wellknownassets.FindContainerdAsset(ig, assetBuilder, arch)
if err != nil && strings.Contains(err.Error(), "unable to parse version string") {
    return fmt.Errorf("containerd.version must look like X.Y.Z: %w", err)
}

Prevention

When it happens

Trigger: FindContainerdAsset falls back to version-based URL building with containerd.Version values like 'latest', 'v2.', '2.1', 'containerd-2.1.0', or any non-semver string.

Common situations: Users set version to a tag-style or marketing string instead of a release number; copy-paste of the full tarball filename into the version field; shell/templating substituting empty or partial values.

Understand the failure class

Related errors


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