googleapis/mcp-toolbox · critical

document %d: %s

Error message

document %d: %s

What it means

When the tools file contains multiple YAML documents (--- separated), a per-document decode failure is reported as `document %d: <yaml error>` with the 1-based document index. It wraps yaml.FormatError output so the offending line inside that document can be located. A single-document file uses the more generic message instead (error 159).

Source

Thrown at internal/server/config.go:197

	// take precedence regardless of document order.
	var toolsetGroups map[string]group.GroupConfig
	// 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. Locate the Nth document (count `---` separators) and fix the reported YAML error inside it
  2. Ensure every document is a YAML mapping with kind/name keys
  3. Temporarily split documents into separate files to isolate the broken one
  4. Validate each document independently with a YAML parser

Example fix

// before
docs:
---
- kind: tool   # document is a list, not a mapping
// after
docs:
---
kind: tool
name: my-tool
Defensive patterns

Strategy: validation

Validate before calling

const docs = fs.readFileSync('tools.yaml','utf8').split(/^---$/m);
docs.forEach((d, i) => {
  const parsed = require('yaml').parse(d);
  if (typeof parsed !== 'object' || Array.isArray(parsed) || parsed === null)
    throw new Error(`document ${i+1} must be a mapping`);
  if (!('kind' in parsed)) throw new Error(`document ${i+1} missing 'kind'`);
});

Prevention

When it happens

Trigger: A multi-document tools.yaml where one document's body cannot be decoded into map[string]any — e.g. a document that is a list/scalar instead of a mapping, or node-level type mismatches during DecodeFromNodeContext.

Common situations: Appending a second config document with `---` that has a structural error, generating configs programmatically producing non-mapping documents, merging config files incorrectly.

Related errors


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