{"record":{"id":"8adf76623ee4df66","repo":"AdguardTeam/AdGuardHome","slug":"persistent-client-at-index-d-unexpected-type-t","errorCode":null,"errorMessage":"persistent client at index %d: unexpected type %T","messagePattern":"persistent client at index (.+?): unexpected type %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/configmigrate/v22.go","lineNumber":53,"sourceCode":"\tdiskConf[\"schema_version\"] = 22\n\n\tconst field = \"blocked_services\"\n\n\tclients, ok, err := fieldVal[yobj](diskConf, \"clients\")\n\tif !ok {\n\t\treturn err\n\t}\n\n\tpersistent, ok, err := fieldVal[yarr](clients, \"persistent\")\n\tif !ok {\n\t\treturn err\n\t}\n\n\tfor i, p := range persistent {\n\t\tvar c yobj\n\t\tc, ok = p.(yobj)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"persistent client at index %d: unexpected type %T\", i, p)\n\t\t}\n\n\t\tvar services yarr\n\t\tservices, ok, err = fieldVal[yarr](c, field)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"persistent client at index %d: %w\", i, err)\n\t\t} else if !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tc[field] = yobj{\n\t\t\t\"ids\": services,\n\t\t\t\"schedule\": yobj{\n\t\t\t\t\"time_zone\": \"Local\",\n\t\t\t},\n\t\t}\n\t}\n","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/AdguardTeam/AdGuardHome/blob/b41aefbe51c8dde65e2c50f093996afa0502edf9/internal/configmigrate/v22.go#L35-L71","documentation":"During migration to schema 22, an element of the persistent clients array is not a YAML object (mapping). Each client must be a map so its \"whois\"/services field can be restructured; scalars or nested arrays abort the migration.","triggerScenarios":"Migrating a config whose clients list contains a string, number, or array element instead of a mapping, e.g. `clients: [ \"laptop\" ]`.","commonSituations":"Hand-rolled configs where a client was written as a plain name, or list-vs-map confusion after editing the YAML.","solutions":["Find the clients array and make every element a mapping (name, ids, etc.)","Remove malformed client entries that are not real client definitions","Re-run the migration"],"exampleFix":"# before\nclients:\n  - \"laptop\"\n\n# after\nclients:\n  - name: laptop\n    ids:\n      - laptop","handlingStrategy":"type-guard","validationCode":"for i, c := range clients {\n    if _, ok := c.(map[string]any); !ok { /* clients[%d] must be a mapping */ _ = i }\n}","typeGuard":"func isObj(v any) bool { _, ok := v.(map[string]any); return ok }","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"expected object\") { /* rewrite offending client entry as a mapping and retry */ }","preventionTips":["Every list of clients/filters should be a list of mappings, never scalars"],"tags":["config","migration","client","yaml","type-mismatch"],"backgroundTag":"config-field-type-mismatch","analyzedSha":"b41aefbe51c8dde65e2c50f093996afa0502edf9","analyzedAt":"2026-08-27T04:57:55.097Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}