kubernetes/kops · error

duplicate image task %q

Error message

duplicate image task %q

What it means

Run() keys each sideload image task as "SideloadImage/" + the base name of the source URL path. If a task with that key already exists in the taskMap, nodeup refuses to overwrite it and errors with "duplicate image task". This prevents two LoadImageTasks fighting over the same image file, usually caused by duplicate image entries in the config.

Source

Thrown at upup/pkg/fi/nodeup/command.go:354

	loader.Builders = append(loader.Builders, &networking.KuberouterBuilder{NodeupModelContext: modelContext})

	loader.Builders = append(loader.Builders, &model.BootstrapClientBuilder{NodeupModelContext: modelContext})
	taskMap, err := loader.Build()
	if err != nil {
		return fmt.Errorf("error building loader: %v", err)
	}

	for _, image := range nodeupConfig.Images[architecture] {
		if len(image.Sources) == 0 {
			return fmt.Errorf("image has no sources: %v", image)
		}
		u, err := url.Parse(image.Sources[0])
		if err != nil {
			return fmt.Errorf("invalid image source URL %q: %w", image.Sources[0], err)
		}
		key := "SideloadImage/" + path.Base(u.Path)
		if _, ok := taskMap[key]; ok {
			return fmt.Errorf("duplicate image task %q", key)
		}
		taskMap[key] = &nodetasks.LoadImageTask{
			Sources: image.Sources,
			Hash:    image.Hash,
		}
	}

	var target fi.NodeupTarget

	switch c.Target {
	case "direct":
		target = &local.LocalTarget{
			CacheDir: c.CacheDir,
			Cloud:    cloud,
		}
	case "dryrun":
		assetBuilder := assets.NewAssetBuilder(vfs.Context, nil, false)
		target = fi.NewNodeupDryRunTarget(assetBuilder, out)

View on GitHub (pinned to 4c8573c808)

Solutions

  1. De-duplicate the image list in nodeupConfig.Images[architecture] so each source filename appears once
  2. If a mirror variant is needed, rename the path segment so base(u.Path) differs (e.g. include a subdirectory or distinct filename)
  3. Regenerate the config with `kops update cluster` after cleaning up custom image overrides in the cluster spec
  4. Check that any config merge/patch tooling is not appending the same image twice

Example fix

// before: two entries whose sources both end in /pause:3.9 -> SideloadImage/pause:3.9 twice
// after: keep one entry or differentiate paths
// "sources": ["https://registry.k8s.io/pause:3.9"]  // remove the duplicate mirror entry
Defensive patterns

Strategy: validation

Validate before calling

seen := map[string]bool{}
for _, img := range nodeupConfig.Images[arch] {
    u, _ := url.Parse(img.Sources[0])
    key := "SideloadImage/" + path.Base(u.Path)
    if seen[key] {
        return fmt.Errorf("duplicate image %s in config", key)
    }
    seen[key] = true
}

Try / catch

if err := nodeupCmd.Run(ctx); err != nil {
    if strings.Contains(err.Error(), "duplicate image task") {
        log.Fatalf("de-duplicate images in NodeupConfig: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: Two or more entries in nodeupConfig.Images[architecture] whose first source URL has the same path base (e.g. same filename from different registries), producing the same SideloadImage/<name> key.

Common situations: The same image listed twice (once from registry.k8s.io, once from a mirror) in containerd images config; merging cluster specs that duplicated an image entry; case/name collisions between custom and default images.

Related errors


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