Summary
When a strategic merge patch replaces a value on an IntOrString field (e.g., maxUnavailable, maxSurge), kustomize preserves the YAML string tag (!!str) from the base node rather than using the tag from the patch node. This causes integer patch values to be emitted as quoted strings in the output, which Kubernetes rejects.
Reproduction
Given a base with an explicitly quoted string value:
# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: test
spec:
strategy:
rollingUpdate:
maxSurge: 50%
maxUnavailable: '6%' # <-- explicitly quoted
And a strategic merge patch with an integer:
# patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: test
spec:
strategy:
rollingUpdate:
maxUnavailable: 3 # <-- integer
# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
patches:
- path: patch.yaml
Expected output
Actual output
Kubernetes rejects this:
The Deployment "test" is invalid: spec.strategy.rollingUpdate.maxUnavailable:
Invalid value: intstr.IntOrString{Type:1, IntVal:0, StrVal:"3"}:
a valid percent string must be a numeric string followed by an ending '%'
Key finding
The behavior depends on whether the base value is explicitly quoted in YAML:
| Base value |
Patch value |
Output |
Valid? |
maxUnavailable: 6% (unquoted) |
3 |
maxUnavailable: 3 |
✅ |
maxUnavailable: '6%' (quoted) |
3 |
maxUnavailable: "3" |
❌ |
maxUnavailable: "6%" (quoted) |
3 |
maxUnavailable: "3" |
❌ |
When the base node has an explicit YAML string tag (from quoting), the strategic merge patch preserves that tag on the replaced value instead of adopting the patch node's tag.
Expected behavior
When a strategic merge patch replaces a scalar value, the output should use the patch node's YAML tag, not the base node's tag. The patch is providing a new value — it should also provide the new type.
Workarounds
- Use a JSON patch (
op: replace) instead of a strategic merge patch — JSON patches correctly emit the integer.
- Remove explicit quotes from the base value (use
6% not '6%').
Environment
- kustomize v5.8.1 (also reproduced with v5.6.0 via go library)
- macOS / Linux
Summary
When a strategic merge patch replaces a value on an
IntOrStringfield (e.g.,maxUnavailable,maxSurge), kustomize preserves the YAML string tag (!!str) from the base node rather than using the tag from the patch node. This causes integer patch values to be emitted as quoted strings in the output, which Kubernetes rejects.Reproduction
Given a base with an explicitly quoted string value:
And a strategic merge patch with an integer:
Expected output
Actual output
Kubernetes rejects this:
Key finding
The behavior depends on whether the base value is explicitly quoted in YAML:
maxUnavailable: 6%(unquoted)3maxUnavailable: 3maxUnavailable: '6%'(quoted)3maxUnavailable: "3"maxUnavailable: "6%"(quoted)3maxUnavailable: "3"When the base node has an explicit YAML string tag (from quoting), the strategic merge patch preserves that tag on the replaced value instead of adopting the patch node's tag.
Expected behavior
When a strategic merge patch replaces a scalar value, the output should use the patch node's YAML tag, not the base node's tag. The patch is providing a new value — it should also provide the new type.
Workarounds
op: replace) instead of a strategic merge patch — JSON patches correctly emit the integer.6%not'6%').Environment