sipeed/picoclaw · error

model_name must be a string, got %T

Error message

model_name must be a string, got %T

What it means

Thrown by mergeModelListsWithMap while merging an array-style model_list with a map-style security model_list during config migration. Each array entry must carry a string model_name because it is used to build the indexed lookup keys ("name" and "name:N") that pair security entries with models. If model_name exists but is not a string after YAML decoding (Go type float64, bool, []any, map[string]any), the merge aborts with the actual Go type in the message.

Source

Thrown at pkg/config/migration.go:506

	delete(m2, "model_list")

	m := mergeMap(m1, m2)
	return m, nil
}

// mergeModelListsWithMap merges array-style model_list with map-style security model_list.
// It generates indexed keys from model_name (like toNameIndex) and uses them
// to look up security entries, falling back to ModelName if the indexed key doesn't exist.
func mergeModelListsWithMap(mainML []any, secML map[string]any) error {
	// Build indexed keys like toNameIndex does
	indexedKeys := make(map[string]int)
	countMap := make(map[string]int)
	for i, m := range mainML {
		if mVal, ok := m.(map[string]any); ok {
			if name, hasName := mVal["model_name"]; hasName {
				nameStr, ok := name.(string)
				if !ok {
					return fmt.Errorf("model_name must be a string, got %T", name)
				}
				index := countMap[nameStr]
				indexedKeys[fmt.Sprintf("%s:%d", nameStr, index)] = i
				if _, ok := indexedKeys[nameStr]; !ok {
					indexedKeys[nameStr] = i
				}
				countMap[nameStr]++
			} else {
				return fmt.Errorf("model_name is required: %#v", mVal)
			}
		}
	}

	for k, v := range secML {
		if i, ok := indexedKeys[k]; ok {
			if vv, ok := v.(map[string]any); ok {
				if mVal, ok := mainML[i].(map[string]any); ok {
					mVal["api_keys"] = vv["api_keys"]

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Quote the value in YAML: `- model_name: "123"` so it decodes as a string
  2. Find the offending entry: the message includes the Go type (float64/bool/map[string]any) which tells you what shape the value has; grep model_list for numeric/boolean/complex model_name values
  3. If the entry was never meant to have model_name at that level, restructure it so model_name is a plain string scalar at the entry top level
  4. Re-run the migration/config load after fixing

Example fix

# before
model_list:
  - model_name: 7b
    provider: openai

# after
model_list:
  - model_name: "7b"
    provider: openai
Defensive patterns

Strategy: validation

Validate before calling

// Pre-validate array-style model_list before triggering the migration/merge.
func validateModelListTypes(ml []any) error {
	for i, m := range ml {
		entry, ok := m.(map[string]any)
		if !ok {
			return fmt.Errorf("model_list[%d]: entry is not a map", i)
		}
		name, has := entry["model_name"]
		if !has {
			return fmt.Errorf("model_list[%d]: model_name is required", i)
		}
		if _, ok := name.(string); !ok {
			return fmt.Errorf("model_list[%d]: model_name must be a string, got %T", i, name)
		}
	}
	return nil
}

Type guard

func isStringModelName(m any) bool {
	entry, ok := m.(map[string]any)
	if !ok { return false }
	name, has := entry["model_name"]
	if !has { return false }
	_, isStr := name.(string)
	return isStr
}

Try / catch

if err := mergeModelListsWithMap(mainML, secML); err != nil {
	// Error text already includes the Go type (%T) or the entry dump (%#v).
	return fmt.Errorf("config migration: fix model_list: %w", err)
}

Prevention

When it happens

Trigger: A model_list entry in YAML like `- model_name: 123`, `- model_name: true`, or `- model_name: [gpt-4]`. YAML scalars decode to non-string Go types, the type assertion `name.(string)` fails, and the error names the offending type (e.g. "got float64").

Common situations: Unquoted numeric model names (`model_name: 7b` decodes as float64), copy-pasted YAML where booleans or inline arrays land on the model_name line, or hand-edited security.yml migration input. Typically surfaces right after upgrading/migrating configs that merge main config with security config.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/ddaca8c73b325be8. Report an issue: GitHub.