2

I have a values.yaml file as follows:

myTest: |-
  - name: app1
    url: https://example.com/app1
  - name: app2
    url: https://example.com/app2

Now I want to deploy a Helm chart which has a template file as follows:

{{- range $i, $item := .Values.myTest | fromYaml }}
apiVersion: v1
kind: ConfigMap
metadata:
  name: config-{{ $item.name }}
data:
  url: {{ $item.url }}
---
{{- end }}

When I try deploying the chart, I get the error:

<$item.name>: can't evaluate field name in type interface {}

How can I resolve this error?

3
  • It looks like you might want to use fromYamlArray instead of fromYaml Commented Sep 24 at 7:16
  • 1
    Can you change the values format to directly contain the list of name/URL pairs, without embedding it in a string? This will be easier to process in your chart and easier to supply from other tools at deployment time. Commented Sep 24 at 10:24
  • 1
    @agilgur5 Thank you for the detailed explanation. Super helpful for future debugging too. Commented Sep 24 at 12:30

1 Answer 1

1

Use fromYamlArray instead of fromYaml

Per my comment, you can fix this by using fromYamlArray instead of fromYaml, since you have an array, not a map structure:

{{- range $i, $item := .Values.myTest | fromYamlArray }}

Debugging the error

<$item.name>: can't evaluate field name in type interface {} 

To help you debug, you can use helm template with the --debug flag, which will print invalid YAML and a trace. For instance:

helm template ./ --debug

This doesn't help with your current template, but if you simplify it a bit for troubleshooting purposes, you can see some hints of the problem:

{{- range $i, $item := .Values.myTest | fromYaml }}
num: {{ $i }}
item: item-{{ $item }}
---
{{- end }}

Re-running the command above, you'll get output of something like:

install.go:178: [debug] Original chart version: ""
install.go:195: [debug] CHART PATH: path-to-your-chart-here

---
# Source: your-filepath-here.yaml

num: Error
item: item-error unmarshaling JSON: while decoding JSON: json: cannot unmarshal array into Go value of type map[string]interface {}
---
Error: YAML parse error on your-filepath-here.yaml: error converting YAML to JSON: yaml: line 2: mapping values are not allowed in this context
helm.go:84: [debug] error converting YAML to JSON: yaml: line 2: mapping values are not allowed in this context

Followed by a stack trace.

Interestingly, you can spot an error in the output:

error unmarshaling JSON: while decoding JSON: json: cannot unmarshal array into Go value of type map[string]interface {}

This swallowed error is saying that your array was unable to be unmarshaled into a map[string]interface {} type that fromYaml expects, which is exactly why you need fromYamlArray instead.

Digging deeper

I'll dive into the output a bit further for educational and debugging purposes.

Looking at the debug output's template closer, it looks like the error message was interpolated into the template. If you simplify the template a bit further:

{{- $items := .Values.myTest | fromYaml }}
items: item-{{ $items }}

and re-run the command again, you'll see:

# [...]
---
# Source: your-filepath-here.yaml

items: items-map[Error:error unmarshaling JSON: while decoding JSON: json: cannot unmarshal array into Go value of type map[string]interface {}]
Error: YAML parse error on your-filepath-here.yaml: error converting YAML to JSON: yaml: mapping values are not allowed in this context
helm.go:84: [debug] error converting YAML to JSON: yaml: mapping values are not allowed in this context
# [...]

From this output we can infer that the type of $items is map[string]string with a key of "Error" with a value of "error unmarshaling JSON...". Or, in Go code:

items := map[string]string{"Error": "error unmarshaling JSON..."};

As this is a map, you actually can iterate through it with range too (like you can with an array). In Go, that would be:

for key, value := range items {
    fmt.Println("Key:", key, "Value:", value)
}

So in your original line:

{{- range $i, $item := .Values.myTest | fromYaml }}

instead of being an index and element of an array, $i and $item are actually the key-value pair of the swallowed error that you get from .Values.myTest | fromYaml. As such, $item.name tries to access the field name in the value, which doesn't exist, resulting in your original error:

<$item.name>: can't evaluate field name in type interface {} 

A bit confusingly, it's a broader type of interface {} instead of string as map[string]interface {} is what fromYaml returns, as mentioned at the end of the prior section.

Parting notes

helm template with the --debug flag can be quite helpful for understanding what went wrong during templating, and you can troubleshoot down to a more minimal template to find some swallowed errors too.
Some knowledge of the implementation language, Go, can also be helpful for deciphering Helm's sometimes cryptic error messages as well. In particular, familiarity with some basic types and data structures as well as common types of marshaling/unmarshaling errors can make them a bit more recognizable (which I certainly learned the hard way myself, having learned Helm before any Go 😅).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.