argoproj/argo-workflows · error

workflow %s expression must evaluate to a string, not a %T

Error message

workflow %s expression must evaluate to a string, not a %T

What it means

Raised by Operation.evaluateStringExpression when a workflowMetadata name/generateName/label/annotation expression evaluates successfully but returns a non-string value (e.g. number, bool, map). Kubernetes metadata fields must be plain strings, so Argo refuses to coerce silently and aborts dispatch with the Go type (%T) it received. This is a distinct, deliberate type check after expr.Run, not a compile/run failure.

Source

Thrown at server/event/dispatch/operation.go:227

		wf.Annotations[annotationKey] = evalAnnotation
	}
	return nil
}

func (o *Operation) evaluateStringExpression(statement string, errorInfo string) (string, error) {
	env := exprenv.GetFuncMap(o.env)
	program, err := expr.Compile(statement, expr.Env(env))
	if err != nil {
		return "", fmt.Errorf("failed to evaluate workflow %s expression: %w", errorInfo, err)
	}
	result, err := expr.Run(program, env)
	if err != nil {
		return "", fmt.Errorf("failed to evaluate workflow %s expression: %w", errorInfo, err)
	}

	v, ok := result.(string)
	if !ok {
		return "", fmt.Errorf("workflow %s expression must evaluate to a string, not a %T", errorInfo, result)
	}
	return v, nil
}

func expressionEnvironment(ctx context.Context, namespace, discriminator string, payload *wfv1.Item) (map[string]any, error) {
	src := map[string]any{
		"namespace":     namespace,
		"discriminator": discriminator,
		"metadata":      metaData(ctx),
		"payload":       payload,
	}
	return jsonutil.Jsonify(src)
}

func metaData(ctx context.Context) map[string]any {
	meta := make(map[string]any)
	md, _ := metadata.FromIncomingContext(ctx)
	for k, v := range md {

View on GitHub (pinned to 35bff19146)

Solutions

  1. Coerce the result to a string in the expression: `string(payload.body.id)`, `payload.body.enabled ? "true" : "false"`, or `json.stringify(...)` / sprig `toString`
  2. Check the %T in the error message to learn which type the expression returned and adjust accordingly
  3. Normalize the producer payload to emit string fields for metadata use
  4. Add a validation step in CI that evaluates each workflowMetadata expression against a sample payload and asserts the result is a string

Example fix

// before (id is a JSON number)
workflowMetadata:
  labels:
    run-id: payload.body.id
// after
workflowMetadata:
  labels:
    run-id: string(payload.body.id)
Defensive patterns

Strategy: type-guard

Validate before calling

// Assert the expression yields a string before using it as metadata
v, ok := result.(string)
if !ok {
    return fmt.Errorf("metadata expression must yield string, got %T", result)
}

Type guard

func isStringResult(result any) bool {
    _, ok := result.(string)
    return ok
}

Prevention

When it happens

Trigger: EventBinding submit.workflowMetadata expression whose result type is not string, e.g. `payload.body.id` where id is a JSON number, `payload.body.enabled` returning bool, or an expression returning a nested object/array — anything that makes the `result.(string)` assertion fail.

Common situations: Event payloads with numeric IDs (int IDs from webhooks) used directly as label values; boolean flags used as annotations; expressions like `payload.body.tags` (array) pasted into labels; users assuming Argo stringifies like Workflow templates do — it does not in this path.

Related errors


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