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
- Remove the existing nodeSelectorTerms match expressions on kubernetes.io/os or kubernetes.io/arch from the manifest's pod affinity before running skaffold
- Use nodeSelector or a differently-keyed affinity term if you need custom scheduling and let skaffold own the os/arch affinity
- 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
- Do not pre-seed os/arch node affinity in source manifests you feed to skaffold
- Keep skaffold-transformed output out of the input path (no feeding rendered output back through render again)
- Use a different label key for custom scheduling constraints
- Review rendered output with 'skaffold render' once to learn which manifests skaffold mutates
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
- empty file found at path: %s, verify that the manifest path
- resource found in %s is not a k8s job, verify the manifest p
- unmarshaling config: %w
- collecting namespaces: %w
- error converting %s to map[string]interface{}
AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05).
Data as JSON: /api/errors/5a10818bfd3369e0.
Report an issue: GitHub.