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
- Ensure the value passed to toYAMLNode (the extension/option value) is a non-nil, marshalled object that produces a valid YAML mapping or sequence.
- Inspect the proto option or extension payload feeding the OpenAPI output; fix empty/invalid values.
- If constructing extensions in code, verify yaml.Marshal of the value produces non-empty output before calling toYAMLNode.
- 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
- Never pass nil or empty values as OpenAPI extension/option payloads.
- Validate proto option payloads referenced by openapiv2 annotations.
- Pin gopkg.in/yaml dependency versions consistently.
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
- only primitive and enum types are allowed in repeated path p
- only primitive and well-known types are allowed in path para
- encountered object type with a summary, but no description
- no description nor summary property
- security: %w
AI-assisted analysis of grpc-ecosystem/grpc-gateway@a58a4436a3 (2026-09-02).
Data as JSON: /api/errors/b983586ca92eeb71.
Report an issue: GitHub.