kubernetes/kops · error

failed to copy image: %v

Error message

failed to copy image: %v

What it means

For any source descriptor whose media type is NOT an index/manifest list, Run assumes it is a single image and calls copyImage (desc.Image() then remote.Write to the target); failures are wrapped as "failed to copy image: %v". This covers both errors building the v1.Image from the source descriptor and errors writing layers/config/manifest to the target registry.

Source

Thrown at pkg/assets/assetcopy/copyimage.go:73

		return fmt.Errorf("fetching %q: %v", source, err)
	}

	targetDesc, err := remote.Get(targetRef, options...)
	if err == nil && desc.Digest.String() == targetDesc.Digest.String() {
		klog.Infof("no need to copy image from %v to %v", sourceRef, targetRef)
		return nil
	}

	switch desc.MediaType {
	case types.OCIImageIndex, types.DockerManifestList:
		// Handle indexes separately.
		if err := copyIndex(desc, sourceRef, targetRef, options...); err != nil {
			return fmt.Errorf("failed to copy index: %v", err)
		}
	default:
		// Assume anything else is an image, since some registries don't set mediaTypes properly.
		if err := copyImage(desc, sourceRef, targetRef, options...); err != nil {
			return fmt.Errorf("failed to copy image: %v", err)
		}
	}

	return nil
}

func copyImage(desc *remote.Descriptor, sourceRef name.Reference, targetRef name.Reference, options ...remote.Option) error {
	klog.Infof("copying image from %v to %v", sourceRef, targetRef)

	img, err := desc.Image()
	if err != nil {
		return err
	}
	return remote.Write(targetRef, img, options...)
}

func copyIndex(desc *remote.Descriptor, sourceRef name.Reference, targetRef name.Reference, options ...remote.Option) error {
	klog.Infof("copying image index from %v to %v", sourceRef, targetRef)

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Check target registry auth: `docker login <target>` and ensure the credentials are in the default keychain kops uses.
  2. Re-run the copy; remote.Write resumes incomplete layer uploads, so transient network errors often resolve on retry.
  3. Verify target registry capacity/quotas and that it accepts the image's media types (schema2/OCI).
  4. Read the inner error to identify the failing blob or manifest; confirm it still exists on the source registry.

Example fix

// before
$ kops ... # failed to copy image: PUT https://myregistry/v2/kops/kube-apiserver/manifests/latest: 403 Forbidden
// after
$ docker login myregistry.example.com
$ # grant push permission to the repo, then re-run kops update cluster
Defensive patterns

Strategy: retry

Validate before calling

if out, err := exec.Command("crane", "digest", sourceImage).CombinedOutput(); err != nil {
	return fmt.Errorf("source %q not pullable: %v: %s", sourceImage, err, out)
}
if err := checkPushAccess(targetRegistry); err != nil {
	return err
}

Try / catch

if err := e.Run(); err != nil {
	if strings.Contains(err.Error(), "failed to copy image") {
		if strings.Contains(err.Error(), "401") || strings.Contains(err.Error(), "403") {
			// fix target-registry credentials, then retry
		} else if isTransient(err) { // net timeout / connection reset
			// retry with backoff; remote.Write resumes partial layer uploads
		}
	}
	return err
}

Prevention

When it happens

Trigger: Single-image (non-index) source; desc.Image() fails to load the manifest/config from the source registry, or remote.Write fails pushing config, layers, or the final manifest to the target: unauthorized, manifest/blob PUT rejected, quota exceeded, connection reset mid-layer upload, or schema-2 media types unsupported by the target.

Common situations: Missing or expired credentials for the private target registry; target registry read-only or over quota; flaky network cutting off a large layer upload; copying very old (schema1) or unusual images into a registry that rejects their media types; pulling from source succeeded earlier but the blob is now gone (retention policy on source).

Related errors


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