grpc-ecosystem/grpc-gateway · error

unexpected number of yaml nodes

Error message

unexpected number of yaml nodes

What it means

toYAMLNode converts a Go value to a YAML node by marshalling it to YAML text and unmarshalling it back into a document node. If the unmarshalled document has zero content nodes (len(doc.Content) == 0), it returns "unexpected number of yaml nodes". This happens when the value marshals to an empty or non-node YAML document, so the generator cannot attach it as an OpenAPI extension value.

Source

Thrown at protoc-gen-openapiv2/internal/genopenapi/generator.go:187

//		var node yaml.Node
//		if err := node.Encode(pio); err != nil {
//			return nil, err
//		}
//		return &node, nil
//	}
func (pio *openapiPathItemObject) toYAMLNode() (*yaml.Node, error) {
	var doc yaml.Node
	var buf bytes.Buffer
	ec := yaml.NewEncoder(&buf)
	ec.SetIndent(2)
	if err := ec.Encode(pio); err != nil {
		return nil, err
	}
	if err := yaml.Unmarshal(buf.Bytes(), &doc); err != nil {
		return nil, err
	}
	if len(doc.Content) == 0 {
		return nil, errors.New("unexpected number of yaml nodes")
	}
	return doc.Content[0], nil
}

func (so openapiInfoObject) MarshalJSON() ([]byte, error) {
	type alias openapiInfoObject
	return extensionMarshalJSON(alias(so), so.extensions)
}

func (so openapiInfoObject) MarshalYAML() (interface{}, error) {
	type Alias openapiInfoObject

	return struct {
		Extension map[string]interface{} `yaml:",inline"`
		Alias     `yaml:",inline"`
	}{
		Extension: extensionsToMap(so.extensions),
		Alias:     Alias(so),

View on GitHub (pinned to a58a4436a3)

Solutions

  1. Ensure the value passed to toYAMLNode (the extension/option value) is a non-nil, marshalled object that produces a valid YAML mapping or sequence.
  2. Inspect the proto option or extension payload feeding the OpenAPI output; fix empty/invalid values.
  3. If constructing extensions in code, verify yaml.Marshal of the value produces non-empty output before calling toYAMLNode.
  4. Check for version mismatches between grpc-gateway and gopkg.in/yaml dependency versions that can affect marshalling.

Example fix

// before
var ext map[string]interface{}
node, err := toYAMLNode(ext) // ext is nil -> empty doc -> error
// after
if len(ext) == 0 {
    return nil
}
node, err := toYAMLNode(ext)
Defensive patterns

Strategy: validation

Validate before calling

buf, _ := yaml.Marshal(v)
if len(bytes.TrimSpace(buf)) == 0 {
    return fmt.Errorf("refusing to convert empty YAML value: %T", v)
}

Type guard

func isNonEmptyYAMLValue(v interface{}) bool {
    if v == nil { return false }
    out, err := yaml.Marshal(v)
    return err == nil && len(bytes.TrimSpace(out)) > 0
}

Try / catch

node, err := toYAMLNode(v)
if err != nil {
    return fmt.Errorf("invalid extension value %v: %w", v, err)
}

Prevention

When it happens

Trigger: Calling toYAMLNode (indirectly via openapiInfoObject/extension handling when building OpenAPI output) with a value that serializes to an empty YAML document — e.g. a nil value, empty document, or malformed extension value used for the OpenAPI spec.

Common situations: Setting an OpenAPI option/extension (via .proto options like openapiv2_swagger or openapiv2_operation, or programmatically) to a value that produces empty YAML output, or feeding generated YAML from another tool that emits nothing.

Related errors


AI-assisted analysis of grpc-ecosystem/grpc-gateway@a58a4436a3 (2026-09-02). Data as JSON: /api/errors/b983586ca92eeb71. Report an issue: GitHub.