Pre-bug-report checklist
1. This bug can be reproduced using pure Argo YAML
If yes, it is more likely to be an Argo bug unrelated to Hera. Please double check before submitting an issue to Hera.
2. I have searched for existing issues
3. This bug occurs in Hera when...
Bug report
Describe the bug
Workflow.to_yaml() / hera._yaml.dump() emit some string values unquoted that go-yaml (used by Argo and kubectl) parses as numbers. PyYAML decides whether to quote a string with its own YAML 1.1 resolver, but go-yaml resolves more plain scalars as numbers, so anything in the gap is written unquoted and changes type on the way to the cluster.
The gap, checked against gopkg.in/yaml.v2 and sigs.k8s.io/yaml (the path kubectl apply takes):
| Hera output (unquoted) |
go-yaml reads it as |
1e3, 1E3, 1e+3, 1e03, +1e3, -1e3 |
float 1000 |
1e-3, 1e-5, 1E-5 |
float 0.001, 1e-05 |
1.5e3, 1.e3 |
float 1500, 1000 |
+.5, -.5 |
float 0.5, -0.5 |
0o17, 0O17, 0X1F |
int 15, 15, 31 |
08 |
float 8 |
1_0e3 |
float 10000 |
PyYAML already quotes the forms its own resolver knows (1.5E-3, .5, 5., 0x1F, 0b11, 1_000, 010, .inf), so this only affects the forms above. The realistic hit is a parameter or env value such as a learning rate "1e-5" or an epoch count "1e3".
Error log if applicable:
With the Argo CRDs from install.yaml (they carry a schema), kubectl apply rejects the exported YAML:
The WorkflowTemplate "yaml-number-repro" is invalid:
* spec.arguments.parameters[0].value: Invalid value: "number": spec.arguments.parameters[0].value in body must be of type string: "number"
* spec.arguments.parameters[1].value: Invalid value: "integer": spec.arguments.parameters[1].value in body must be of type string: "integer"
* spec.templates[0].container.env[0].value: Invalid value: "number": spec.templates[0].container.env[0].value in body must be of type string: "number"
With the minimal CRDs (no schema) the apply succeeds and the values are stored with the wrong type: learning-rate becomes the float 1e-05, epochs becomes the int 1000, LEARNING_RATE becomes the float 1e-05.
To Reproduce
Full Hera code to reproduce the bug:
from hera.workflows import Container, Env, Parameter, WorkflowTemplate
with WorkflowTemplate(
name="yaml-number-repro",
entrypoint="train",
arguments=[Parameter(name="learning-rate", value="1e-5"), Parameter(name="epochs", value="1e3")],
) as wt:
Container(
name="train",
image="python:3.12-alpine",
command=["python", "-c", "print('ok')"],
env=[Env(name="LEARNING_RATE", value="1e-5")],
)
print(wt.to_yaml())
Output (values unquoted):
env:
- name: LEARNING_RATE
value: 1e-5
arguments:
parameters:
- name: learning-rate
value: 1e-5
- name: epochs
value: 1e3
Then kubectl apply -f on that output gives the error above (Argo v4.1.2 CRDs on a kind cluster).
Expected behavior
Strings that go-yaml would read as a number should be quoted, the same way y/n are quoted since #1598:
env:
- name: LEARNING_RATE
value: '1e-5'
arguments:
parameters:
- name: learning-rate
value: '1e-5'
- name: epochs
value: '1e3'
With that output kubectl apply succeeds and the values are stored as strings.
Environment
- Hera Version: main (
ee518f5, after 7.1.0)
- Python Version: 3.14.4 (PyYAML 6.0.3)
- Argo Version: 4.1.2 CRDs,
kubectl 1.37
Additional context
go-yaml resolves a plain scalar as a number when, after removing underscores, strconv.ParseInt(s, 0, 64) succeeds or the scalar matches its yamlStyleFloat regex ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ (see resolve.go in gopkg.in/yaml.v2). The str_presenter in src/hera/_yaml.py can mirror those two rules and single-quote matching strings. Quoting a string is always safe for Argo, so erring on the side of quoting does not change any value. I have a PR ready with the fix and tests.
Pre-bug-report checklist
1. This bug can be reproduced using pure Argo YAML
If yes, it is more likely to be an Argo bug unrelated to Hera. Please double check before submitting an issue to Hera.
2. I have searched for existing issues
emits unquotedy/Y/n/N` strings that Argo/kubectl parse as booleans聽#1597 / Fix YAML dump quoting for y/Y/n/N boolean aliases聽#1598, which fixed they/Y/n/Nboolean case in the same representer)3. This bug occurs in Hera when...
Bug report
Describe the bug
Workflow.to_yaml()/hera._yaml.dump()emit some string values unquoted that go-yaml (used by Argo andkubectl) parses as numbers. PyYAML decides whether to quote a string with its own YAML 1.1 resolver, but go-yaml resolves more plain scalars as numbers, so anything in the gap is written unquoted and changes type on the way to the cluster.The gap, checked against
gopkg.in/yaml.v2andsigs.k8s.io/yaml(the pathkubectl applytakes):1e3,1E3,1e+3,1e03,+1e3,-1e310001e-3,1e-5,1E-50.001,1e-051.5e3,1.e31500,1000+.5,-.50.5,-0.50o17,0O17,0X1F15,15,310881_0e310000PyYAML already quotes the forms its own resolver knows (
1.5E-3,.5,5.,0x1F,0b11,1_000,010,.inf), so this only affects the forms above. The realistic hit is a parameter or env value such as a learning rate"1e-5"or an epoch count"1e3".Error log if applicable:
With the Argo CRDs from
install.yaml(they carry a schema),kubectl applyrejects the exported YAML:With the minimal CRDs (no schema) the apply succeeds and the values are stored with the wrong type:
learning-ratebecomes the float1e-05,epochsbecomes the int1000,LEARNING_RATEbecomes the float1e-05.To Reproduce
Full Hera code to reproduce the bug:
Output (values unquoted):
Then
kubectl apply -fon that output gives the error above (Argo v4.1.2 CRDs on a kind cluster).Expected behavior
Strings that go-yaml would read as a number should be quoted, the same way
y/nare quoted since #1598:With that output
kubectl applysucceeds and the values are stored as strings.Environment
ee518f5, after 7.1.0)kubectl1.37Additional context
go-yaml resolves a plain scalar as a number when, after removing underscores,
strconv.ParseInt(s, 0, 64)succeeds or the scalar matches itsyamlStyleFloatregex^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$(seeresolve.goingopkg.in/yaml.v2). Thestr_presenterinsrc/hera/_yaml.pycan mirror those two rules and single-quote matching strings. Quoting a string is always safe for Argo, so erring on the side of quoting does not change any value. I have a PR ready with the fix and tests.