GoogleContainerTools/skaffold · error

found existing node affinity for os/arch: %s

Error message

found existing node affinity for os/arch: %s

What it means

Skaffold's updateAffinity scans a workload's pod-template node affinity for pre-existing match expressions on the kubernetes.io/os or kubernetes.io/arch node labels. If any already exist, it refuses to add its own os/arch affinity to avoid conflicting or duplicate selector terms, and returns this error. It is a deliberate guard: only one os/arch affinity management owner is supported.

Source

Thrown at pkg/skaffold/kubernetes/manifest/affinity.go:219

		if err != nil {
			return nil, err
		}
		if err = json.Unmarshal(data, &affinity); err != nil {
			return nil, err
		}
	}

	if affinity.NodeAffinity == nil {
		affinity.NodeAffinity = &v1.NodeAffinity{}
	}
	if affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
		affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution = &v1.NodeSelector{}
	}

	for _, term := range affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms {
		for _, exp := range term.MatchExpressions {
			if exp.Key == nodeOperatingSystemLabel || exp.Key == nodeArchitectureLabel {
				return nil, fmt.Errorf("found existing node affinity for os/arch: %s", exp)
			}
		}
	}

	if len(affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms) == 0 {
		affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms = append(affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms, v1.NodeSelectorTerm{})
	}

	var terms []v1.NodeSelectorTerm
	for _, pl := range platforms {
		for _, term := range affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms {
			t := term.DeepCopy()
			if pl.OS != "" {
				t.MatchExpressions = append(t.MatchExpressions, v1.NodeSelectorRequirement{
					Key:      nodeOperatingSystemLabel,
					Operator: v1.NodeSelectorOpIn,
					Values:   []string{pl.OS},
				})

View on GitHub (pinned to a1189de023)

Solutions

  1. Remove the existing nodeSelectorTerms match expressions on kubernetes.io/os or kubernetes.io/arch from the manifest's pod affinity before running skaffold
  2. Use nodeSelector or a differently-keyed affinity term if you need custom scheduling and let skaffold own the os/arch affinity
  3. Disable skaffold's affinity transformation in your render/deploy configuration so it does not try to patch affinity

Example fix

// before
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/arch
          operator: In
          values: [amd64]
// after (let skaffold add it)
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: some-other-label
          operator: In
          values: [foo]
Defensive patterns

Strategy: validation

Validate before calling

func hasOSArchAffinity(podSpec map[string]interface{}) bool {
  aff, ok := podSpec["affinity"].(map[string]interface{}); if !ok { return false }
  na, ok := aff["nodeAffinity"].(map[string]interface{}); if !ok { return false }
  req, ok := na["requiredDuringSchedulingIgnoredDuringExecution"].(map[string]interface{}); if !ok { return false }
  terms, _ := req["nodeSelectorTerms"].([]interface{})
  for _, t := range terms {
    tm, _ := t.(map[string]interface{})
    exprs, _ := tm["matchExpressions"].([]interface{})
    for _, e := range exprs {
      em, _ := e.(map[string]interface{})
      if k, _ := em["key"].(string); k == "kubernetes.io/os" || k == "kubernetes.io/arch" { return true }
    }
  }
  return false
}
// skip skaffold affinity patching if hasOSArchAffinity(podSpec) is true

Type guard

isOSArchKey := func(exp map[string]interface{}) bool {
  k, _ := exp["key"].(string)
  return k == "kubernetes.io/os" || k == "kubernetes.io/arch"
}

Prevention

When it happens

Trigger: Calling ManifestList.Visit (via the affinity transformer) on a manifest whose pod spec affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution already contains a MatchExpressions entry with Key 'kubernetes.io/os' or 'kubernetes.io/arch'.

Common situations: Deploying a manifest that was previously processed by skaffold's affinity patching (output fed back in), hand-written manifests that pin os/arch node affinity, or Helm/kustomize outputs embedding such selectors.

Related errors


AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05). Data as JSON: /api/errors/5a10818bfd3369e0. Report an issue: GitHub.