kubernetes/kops · error

%s: %v

Error message

%s: %v

What it means

Copy() runs each asset copy task in a bounded worker-slot goroutine (cap-5 buffered channel used as a semaphore). When a task's Run() fails, the error is wrapped with the asset name via fmt.Errorf("%s: %v", n, err) before being sent on the channel, so the klog warning names which asset failed. It is a wrapper, not a root cause; the underlying error follows the colon.

Solutions

  1. Read the asset name prefix, then diagnose the underlying error after the colon (often 'unable to transfer ...' or an upload/write error).
  2. Verify the source asset URL is reachable from the machine running kops (curl/HEAD the canonical URL).
  3. Re-run `kops get assets --copy`; the channel-based task loop retries only on a fresh invocation.
  4. If the same asset fails repeatedly, check the asset's sha in the cluster spec against the actual upstream file.

Example fix

// before (observed log)
W  https://storage.googleapis.com/k8s-artifacts-prod/kube-proxy.tar.gz: unable to transfer ... : context deadline exceeded
// after (fix upstream cause, e.g. retry with network access restored)
kops get assets --copy --output <spec>  # succeeds once source is reachable
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check that each asset's source URL is reachable before copying:
for _, name := range assetNames {
    resp, err := http.Head(sourceURL(name))
    if err != nil || resp.StatusCode >= 400 {
        log.Printf("asset %s unreachable: %v", name, err)
    }
}

Type guard

// Go has no runtime type guard needed; narrow the wrapped error by inspecting the message prefix:
func assetNameFromError(err error) (string, bool) {
    if err == nil { return "", false }
    if i := strings.Index(err.Error(), ": "); i > 0 { return err.Error()[:i], true }
    return "", false
}

Try / catch

if err := assetcopy.Copy(imageAssets, fileAssets, vfsContext, cluster); err != nil {
    // per-asset failures were already klog.Warning'd with "<asset>: <cause>"
    return fmt.Errorf("asset copy incomplete, see warnings above: %w", err)
}

Prevention

When it happens

Trigger: Any failure returned by CopyFile.Run() or CopyImage.Run() during `kops get assets --copy` — e.g. download failure, sha mismatch, or upload failure — is prefixed with the asset's canonical URL/name at copy.go:107.

Common situations: A single unreachable file repository or container registry among many assets; one corrupted asset whose sha does not match; transient network errors while copying hundreds of assets.

Related errors


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

Appendix: source

Thrown at pkg/assets/assetcopy/copy.go:107

	}

	gotError := false
	names := make([]string, 0, len(tasks))
	for name := range tasks {
		names = append(names, name)
	}
	sort.Strings(names)
	for _, name := range names {
		task := tasks[name]
		err := <-ch
		if err != nil {
			klog.Warning(err)
			gotError = true
		}
		go func(n string, t assetTask) {
			err := t.Run()
			if err != nil {
				err = fmt.Errorf("%s: %v", n, err)
			}
			ch <- err
		}(name, task)
	}

	for i := 0; i < cap(ch); i++ {
		err := <-ch
		if err != nil {
			klog.Warning(err)
			gotError = true
		}
	}

	close(ch)
	if gotError {
		return fmt.Errorf("not all assets copied successfully")
	}
	return nil

View on GitHub (pinned to 4c8573c808)