googleapis/mcp-toolbox · error

doc %d: invalid config format at key %q: expected nested for

Error message

doc %d: invalid config format at key %q: expected nested format keys and type map

What it means

ConvertConfig returns this when a top-level key in a config document is neither in already-flat format nor the expected nested format (a keys-and-type map). Migration only understands two shapes; anything else — a scalar, a bare list at the wrong level, or an unrecognized structure — is rejected. The doc index and offending key are reported.

Source

Thrown at cmd/internal/config.go:288

				case "toolsets":
					key = "toolset"
				case "prompts":
					key = "prompt"
				case "groups":
					key = "group"
				}
				transformed, err := transformDocs(key, slice)
				if err != nil {
					return nil, fmt.Errorf("doc %d: invalid config format at key %q: %w", docIndex, srcKey, err)
				}
				// encode per-doc
				for _, doc := range transformed {
					if err := encoder.Encode(migrateToolsetKind(ctx, doc)); err != nil {
						return nil, err
					}
				}
			} else {
				return nil, fmt.Errorf("doc %d: invalid config format at key %q: expected nested format keys and type map", docIndex, key)
			}
		}
	}
	return buf.Bytes(), nil
}

// hasKindField is a helper function to check if an input is in flat format
func hasKindField(input yaml.MapSlice) bool {
	for _, item := range input {
		if key, ok := item.Key.(string); ok && key == "kind" {
			return true
		}
	}
	return false
}

// migrateToolsetKind rewrites `kind: toolset` to `kind: group`, preserving field
// order, and returns other kinds unchanged. Every flat doc passes through here,

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Change the key's value to a proper mapping: either the current flat schema or the legacy nested keys-and-type map.
  2. Check that the section (sources/tools/etc.) is a map whose entries contain a 'kind' and parameters.
  3. Regenerate the config from the current toolbox docs/templates instead of editing a broken file.
  4. Use the migration command on the original legacy config to produce a valid flat file.

Example fix

# before
sources:
  - mydb
# after
sources:
  mydb:
    kind: postgres
    host: localhost
    port: 5432
    database: appdb
    user: ${DB_USER}
    password: ${DB_PASSWORD}
Defensive patterns

Strategy: validation

Validate before calling

var doc map[string]yaml.Node
if err := node.Decode(&doc); err != nil { log.Fatal(err) }
for key, val := range doc {
    if val.Kind != yaml.MappingNode { log.Fatalf("key %q must map to a section map", key) }
}

Type guard

func isSectionMap(n *yaml.Node) bool {
    return n != nil && n.Kind == yaml.MappingNode
}

Try / catch

if strings.Contains(err.Error(), "expected nested format keys and type map") {
    log.Fatalf("section must be a map (legacy nested or flat format): %v", err)
}

Prevention

When it happens

Trigger: A config document has a key like `sources:` or `tools:` whose value is not a map of sub-keys with type maps (nested legacy) and not already-flat docs — e.g. `sources: []` or `sources: mydb` encountered in ConvertConfig.

Common situations: Typing `sources:` with a list instead of a map; collapsing a section to a scalar; truncating or hand-editing configs; using a config from an incompatible/unrelated schema.

Related errors


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