googleapis/mcp-toolbox · critical

unable to decode YAML document: %s

Error message

unable to decode YAML document: %s

What it means

When the tools file has a single YAML document and yaml.Decoder.DecodeFromNodeContext fails to decode it into a resource map, the config loader aborts with `unable to decode YAML document: <formatted error>`. Unlike the multi-document case, no document index is shown since there is only one.

Source

Thrown at internal/server/config.go:199

	// promptset configs is not yet supported

	file, err := parser.ParseBytes(raw, 0)
	if err != nil {
		return nil, nil, nil, nil, nil, nil, fmt.Errorf("unable to parse YAML: %s", yaml.FormatError(err, false, false))
	}

	decoder := yaml.NewDecoder(bytes.NewReader(raw))
	for index, doc := range file.Docs {
		if doc == nil || doc.Body == nil {
			continue
		}
		docIndex := index + 1
		var resource map[string]any
		if err := decoder.DecodeFromNodeContext(ctx, doc.Body, &resource); err != nil {
			if len(file.Docs) > 1 {
				return nil, nil, nil, nil, nil, nil, fmt.Errorf("document %d: %s", docIndex, yaml.FormatError(err, false, false))
			}
			return nil, nil, nil, nil, nil, nil, fmt.Errorf("unable to decode YAML document: %s", yaml.FormatError(err, false, false))
		}
		var kind, name string
		var ok bool
		if kind, ok = resource["kind"].(string); !ok {
			if len(file.Docs) > 1 {
				return nil, nil, nil, nil, nil, nil, fmt.Errorf("%s missing 'kind' field or it is not a string", formatDocLocation(docIndex, keyToken(doc.Body, "kind"), doc.Body))
			}
			return nil, nil, nil, nil, nil, nil, fmt.Errorf("missing 'kind' field or it is not a string: %v", resource)
		}
		if name, ok = resource["name"].(string); !ok {
			// A `kind: group` may omit `name` to target the default nameless group;
			// every other resource requires a name.
			if kind == "group" {
				if rawName, present := resource["name"]; !present || rawName == nil {
					name, ok = "", true
				}
			}
		}

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Ensure the top level of the file is a mapping (sources:, tools:, etc.), not a list or scalar
  2. Restore the full file structure (kind/name on resources) from the docs examples
  3. Check the embedded yaml.FormatError message for the failing line/column
  4. Validate the file parses to an object: `yq eval '.' tools.yaml`

Example fix

// before
- kind: source
  name: my-pg
// after
sources:
  my-pg:
    kind: postgres
Defensive patterns

Strategy: validation

Validate before calling

const parsed = require('yaml').parse(fs.readFileSync('tools.yaml','utf8'));
if (typeof parsed !== 'object' || Array.isArray(parsed) || parsed === null) {
  throw new Error('tools.yaml root must be a mapping (sources:, tools:, ...)');
}

Prevention

When it happens

Trigger: Loading a single-document tools.yaml whose root is not decodable into map[string]any (e.g. the file starts with a list or scalar), or YAML content that parses but fails node decoding due to duplicate keys/type conflicts.

Common situations: Replacing tools.yaml contents with a plain list of tools, truncating the file so only a fragment remains, accidentally saving logs or JSON into the tools file.

Understand the failure class

Related errors


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/ea8150e341a879f0. Report an issue: GitHub.