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
- Quote the value in YAML: `- model_name: "123"` so it decodes as a string
- 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
- 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
- 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
- Quote numeric-looking model names in YAML ("7b", "123") so they decode as strings
- Lint model_list entries in CI with a schema check before migration runs
- Never nest lists/maps under model_name
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
- model_name is required: %#v
- failed to parse security config %s: %w
- failed to parse legacy skills security config: %w
- invalid WeCom QR generate URL: %w
- invalid WeCom QR query URL: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/ddaca8c73b325be8.
Report an issue: GitHub.