{"record":{"id":"1bc71f88617cd8cd","repo":"AdguardTeam/AdGuardHome","slug":"filters-at-index-d-expected-object-got-t","errorCode":null,"errorMessage":"filters: at index %d: expected object, got %T","messagePattern":"filters: at index (.+?): expected object, got %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/configmigrate/v29.go","lineNumber":46,"sourceCode":"//\t    - '/path/to/file.txt'\n//\t  # …\nfunc (m Migrator) migrateTo29(_ context.Context, diskConf yobj) (err error) {\n\tdiskConf[\"schema_version\"] = 29\n\n\tfilterVals, ok, err := fieldVal[[]any](diskConf, \"filters\")\n\tif !ok {\n\t\treturn err\n\t}\n\n\tpaths := []string{\n\t\tfilepath.Join(m.dataDir, \"userfilters\", \"*\"),\n\t}\n\n\tfor i, v := range filterVals {\n\t\tvar f yobj\n\t\tf, ok = v.(yobj)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"filters: at index %d: expected object, got %T\", i, v)\n\t\t}\n\n\t\tvar u string\n\t\tu, ok, _ = fieldVal[string](f, \"url\")\n\t\tif ok && filepath.IsAbs(u) {\n\t\t\tpaths = append(paths, u)\n\t\t}\n\t}\n\n\tfltConf, ok, err := fieldVal[yobj](diskConf, \"filtering\")\n\tif !ok {\n\t\treturn err\n\t}\n\n\tfltConf[\"safe_fs_patterns\"] = paths\n\n\treturn nil\n}","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/AdguardTeam/AdGuardHome/blob/b41aefbe51c8dde65e2c50f093996afa0502edf9/internal/configmigrate/v29.go#L28-L64","documentation":"During migration to schema 29, an element of the filters array is not a YAML object. Each filter must be a mapping so its url field can be inspected (and rewritten if it's an absolute local path); scalars or nested arrays abort the migration.","triggerScenarios":"Migrating a config where filters contains a non-object entry, e.g. `- https://example.org/hosts` (bare string) instead of `- url: https://example.org/hosts, enabled: true`.","commonSituations":"Hand-converted configs where filter URLs were written as plain list items; merging filter lists from different sources with inconsistent shapes.","solutions":["Rewrite every filters entry as a mapping with url/enabled keys","Delete malformed entries that aren't real filter definitions","Re-run Migrate"],"exampleFix":"# before\nfilters:\n  - https://example.org/hosts\n\n# after\nfilters:\n  - enabled: true\n    url: https://example.org/hosts","handlingStrategy":"type-guard","validationCode":"for _, f := range filters {\n    if _, ok := f.(map[string]any); !ok { /* rewrite as {url: ..., enabled: true} */ }\n}","typeGuard":"func isFilterObj(v any) bool { _, ok := v.(map[string]any); return ok }","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"expected object, got\") { /* convert bare-string filters to objects and retry */ }","preventionTips":["Each filter entry needs url and enabled keys"],"tags":["config","migration","filters","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"}