Skip to content

Workflow.to_yaml() emits unquoted strings like 1e-5 that Argo/kubectl parse as numbers聽#1607

Description

@PhuocOng

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...

  • exporting to YAML
  • submitting to Argo
  • running on Argo with the Hera runner
  • other:

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions