mikefarah/yq · error

cannot use %v as attribute, only scalars are supported

Error message

cannot use %v as attribute, only scalars are supported

What it means

XML attributes (keys starting with the attribute prefix, default `+` or `@a`) must be scalar values because XML attributes can only hold text. If a key like `+attr` maps to a map/sequence, encodeMap rejects it with this message naming the offending tag.

Source

Thrown at pkg/yqlib/encoder_xml.go:276

		name != e.prefs.ContentName &&
		name != e.prefs.DirectiveName &&
		!strings.HasPrefix(name, e.prefs.ProcInstPrefix)
}

func (e *xmlEncoder) encodeMap(encoder *xml.Encoder, node *CandidateNode, start xml.StartElement) error {
	log.Debug("its a map")

	//first find all the attributes and put them on the start token
	for i := 0; i < len(node.Content); i += 2 {
		key := node.Content[i]
		value := node.Content[i+1]

		if e.isAttribute(key.Value) {
			if value.Kind == ScalarNode {
				attributeName := strings.Replace(key.Value, e.prefs.AttributePrefix, "", 1)
				start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: attributeName}, Value: value.Value})
			} else {
				return fmt.Errorf("cannot use %v as attribute, only scalars are supported", value.Tag)
			}
		}
	}

	err := e.encodeStart(encoder, node, start)
	if err != nil {
		return err
	}

	//now we encode non attribute tokens
	for i := 0; i < len(node.Content); i += 2 {
		key := node.Content[i]
		value := node.Content[i+1]

		err := e.encodeComment(encoder, headAndLineComment(key))
		if err != nil {
			return err
		}

View on GitHub (pinned to 8b5af0694b)

Solutions

  1. Make the attribute value a scalar: `+attr: value`.
  2. Move the complex data to a child element (non-attribute key or the content key) instead of an attribute.
  3. Rename the key so it no longer starts with the attribute prefix if it is not meant to be an attribute.
  4. Change --xml-attribute-prefix so ordinary keys are not misinterpreted as attributes.

Example fix

// before
root:
  +attrs: {a: 1}
// after
root:
  +a: 1
  content: {a: 1}
Defensive patterns

Strategy: validation

Validate before calling

yq '.root | to_entries | select(.key | test("^\\+")) | any(.value | kind != "scalar")' in.yaml

Type guard

// Go
func attributesAreScalar(m *yqlib.CandidateNode, prefix string) bool {
    for i := 0; i < len(m.Content); i += 2 {
        k, v := m.Content[i].Value, m.Content[i+1]
        if strings.HasPrefix(k, prefix) && v.Kind != yqlib.ScalarNode { return false }
    }
    return true
}

Try / catch

// Go
if err := enc.Encode(w, node); err != nil {
    if strings.Contains(err.Error(), "only scalars are supported") {
        // restructure attribute into a child element and retry
    }
}

Prevention

When it happens

Trigger: Encoding `-o xml` a document such as `root: +attr: {a: 1}` or `+attr: [1,2]` — an attribute-prefixed key whose value is a MappingNode or SequenceNode.

Common situations: Round-tripping XML that originally had attributes with complex content; hand-authored YAML where an attribute-prefixed key was given a nested structure; tool-generated docs mixing attribute and content conventions.

Related errors


AI-assisted analysis of mikefarah/yq@8b5af0694b (2026-09-05). Data as JSON: /api/errors/ae1e11bdfb33ec7a. Report an issue: GitHub.