{"record":{"id":"c642b55ec9995f08","repo":"AdguardTeam/AdGuardHome","slug":"invalid-upstream-servers-w","errorCode":null,"errorMessage":"invalid upstream servers: %w","messagePattern":"invalid upstream servers: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/persistent.go","lineNumber":150,"sourceCode":"}\n\n// validate returns an error if persistent client information contains errors.\n// allTags must be sorted.\nfunc (c *Persistent) validate(ctx context.Context, l *slog.Logger, allTags []string) (err error) {\n\tswitch {\n\tcase c.Name == \"\":\n\t\treturn errors.Error(\"empty name\")\n\tcase c.idendifiersLen() == 0:\n\t\treturn errors.Error(\"id required\")\n\tcase c.UID == UID{}:\n\t\treturn errors.Error(\"uid required\")\n\t}\n\n\tconf, err := proxy.ParseUpstreamsConfig(c.Upstreams, &upstream.Options{\n\t\tLogger: l.With(aghslog.KeyUpstreamType, aghslog.UpstreamTypeTest),\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid upstream servers: %w\", err)\n\t}\n\n\terr = conf.Close()\n\tif err != nil {\n\t\tl.ErrorContext(ctx, \"client: closing upstream config\", slogutil.KeyError, err)\n\t}\n\n\tfor _, t := range c.Tags {\n\t\t_, ok := slices.BinarySearch(allTags, t)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"invalid tag: %q\", t)\n\t\t}\n\t}\n\n\t// TODO(s.chzhen):  Move to the constructor.\n\tslices.Sort(c.Tags)\n\n\treturn nil","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/AdguardTeam/AdGuardHome/blob/b41aefbe51c8dde65e2c50f093996afa0502edf9/internal/client/persistent.go#L132-L168","documentation":"Validation error raised while adding or updating a persistent client: the client's Upstreams strings could not be parsed into valid upstream server configs. The underlying proxy.ParseUpstreamsConfig error is wrapped, so the cause (bad URL, unsupported scheme, bad port) is visible in the chain.","triggerScenarios":"Calling Storage.Add or Storage.Update with c.Upstreams containing an invalid upstream spec, e.g. \"[::1]:bad\", \"htp://example.com\", or a malformed dns:// URL.","commonSituations":"Typos in per-client upstream strings, using a scheme the proxy package doesn't support, missing port on non-default schemes, trailing whitespace or quotes after YAML editing.","solutions":["Read the wrapped cause in the error chain to identify the offending upstream string","Fix the URL: valid scheme (dns://, https://, tcp://, quic://, tls://) and correct host:port","Test the upstream string with proxy.ParseUpstreamsConfig directly before calling Add"],"exampleFix":"// before\nc.Upstreams = []string{\"dns.example.com\"} // missing scheme/port\n\n// after\nc.Upstreams = []string{\"dns://dns.example.com:53\"}","handlingStrategy":"validation","validationCode":"_, err := proxy.ParseUpstreamsConfig(c.Upstreams, &upstream.Options{Logger: testLogger})\nif err != nil { /* reject before Add/Update */ }","typeGuard":null,"tryCatchPattern":"if err := s.Add(ctx, c); err != nil && strings.Contains(err.Error(), \"invalid upstream servers\") { /* surface wrapped cause to user for correction */ }","preventionTips":["Always include scheme and port: dns://host:53, https://host:443","Validate upstream strings with ParseUpstreamsConfig in form handlers before persisting"],"tags":["client","upstream","dns","validation","config"],"backgroundTag":"invalid-upstream-config","analyzedSha":"b41aefbe51c8dde65e2c50f093996afa0502edf9","analyzedAt":"2026-08-27T04:57:55.097Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}