argoproj/argo-workflows · error

no text to unmarshal

Error message

no text to unmarshal

What it means

MustUnmarshal is a helper in pkg/apis/workflow/v1alpha1/marshall.go that unmarshals YAML/JSON text into a value, supporting string/[]byte input and '@filename' file references. It panics with "no text to unmarshal" when given an empty []byte, because there is nothing to parse. It is intended for call sites where the input is known-good (tests, codegen); a panic signals a programming bug, not a runtime condition.

Source

Thrown at pkg/apis/workflow/v1alpha1/marshall.go:21

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"

	"sigs.k8s.io/yaml"
)

// MustUnmarshal is a utility function to unmarshall either a file, byte array, or string of JSON or YAMl into a object.
// text - a byte array or string, if starts with "@" it assumed to be a file and read from disk, is starts with "{" assumed to be JSON, otherwise assumed to be YAML
// v - a pointer to an object
func MustUnmarshal(text, v any) {
	switch x := text.(type) {
	case string:
		MustUnmarshal([]byte(x), v)
	case []byte:
		if len(x) == 0 {
			panic("no text to unmarshal")
		}
		switch x[0] {
		case '@':
			filename := string(x[1:])
			y, err := os.ReadFile(filepath.Clean(filename))
			if err != nil {
				panic(fmt.Errorf("failed to read file %s: %w", filename, err))
			}
			MustUnmarshal(y, v)
		case '{':
			if err := json.Unmarshal(x, v); err != nil {
				panic(fmt.Errorf("failed to unmarshal JSON %q: %w", string(x), err))
			}
		default:
			if err := yaml.UnmarshalStrict(x, v); err != nil {
				panic(fmt.Errorf("failed to unmarshal YAML %q: %w", string(x), err))
			}
		}

View on GitHub (pinned to 35bff19146)

Solutions

  1. Verify the text argument is non-empty before calling MustUnmarshal; log/inspect the source of the string
  2. Use the error-returning variant (unmarshal helper in the same file) if the input can legitimately be empty
  3. Guard the call site: if len(data)==0 { initialize v with defaults instead of unmarshalling }
  4. If loading from a file via '@path', confirm the file exists and is non-empty

Example fix

// before
item := MustUnmarshal([]byte(os.Getenv("WF_SPEC")), &Item{})
// after
text := os.Getenv("WF_SPEC")
if text == "" {
    panic("WF_SPEC env var is empty")
}
item := MustUnmarshal([]byte(text), &Item{})
Defensive patterns

Strategy: validation

Validate before calling

if text == nil || len(text.([]byte)) == 0 {
    return fmt.Errorf("refusing MustUnmarshal: empty input")
}
MustUnmarshal(text, v)

Try / catch

func() (item *Item, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("MustUnmarshal panicked: %v", r)
        }
    }()
    MustUnmarshal(data, &item)
    return item, nil
}()

Prevention

When it happens

Trigger: Calling MustUnmarshal with an empty string coerced to []byte (len==0), e.g. MustUnmarshal([]byte(""), &item), or a variable that was expected to hold YAML/JSON but is empty.

Common situations: Test fixtures where a heredoc/env variable/secret came back empty; reading config that failed silently upstream and passed an empty slice; tests like TestItem_GetMapVal/GetListVal/GetStrVal passing blank input by mistake.

Related errors


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/535b5a6eeb511021. Report an issue: GitHub.