{"record":{"id":"a922b403391bd49e","repo":"juanfont/headscale","slug":"trusted-proxies-d-q-w","errorCode":null,"errorMessage":"trusted_proxies[%d] %q: %w","messagePattern":"trusted_proxies\\[(.+?)\\] %q: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/types/config.go","lineNumber":1076,"sourceCode":"\n\tipSet, _ := builder.IPSet()\n\n\treturn &prefix, !ipSet.ContainsPrefix(prefix), nil\n}\n\n// trustedProxies rejects 0.0.0.0/0 and ::/0 because they defeat the\n// peer-trust gate and almost always indicate misconfiguration.\nfunc trustedProxies() ([]netip.Prefix, error) {\n\traw := viper.GetStringSlice(\"trusted_proxies\")\n\tif len(raw) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tout := make([]netip.Prefix, 0, len(raw))\n\tfor i, s := range raw {\n\t\tp, err := netip.ParsePrefix(s)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"trusted_proxies[%d] %q: %w\", i, s, err)\n\t\t}\n\n\t\tif p.Bits() == 0 {\n\t\t\treturn nil, fmt.Errorf(\"trusted_proxies[%d] %q: %w\", i, s, errTrustedProxyZeroRange)\n\t\t}\n\n\t\tout = append(out, p.Masked())\n\t}\n\n\treturn out, nil\n}\n\n// LoadCLIConfig returns the needed configuration for the CLI client\n// of Headscale to connect to a Headscale server.\nfunc LoadCLIConfig() (*Config, error) {\n\tlogConfig := logConfig()\n\tzerolog.SetGlobalLevel(logConfig.Level)\n","sourceCodeStart":1058,"sourceCodeEnd":1094,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/types/config.go#L1058-L1094","documentation":"Thrown while parsing the trusted_proxies configuration list. Each entry must be a valid CIDR prefix parseable by netip.ParsePrefix; when entry i fails to parse, its index, raw string, and the underlying parse error are wrapped and returned. This aborts server startup so that a typo in the proxy list cannot silently disable X-Forwarded-For handling.","triggerScenarios":"Setting trusted_proxies in config (e.g. trusted_proxies: [\"10.0.0.1/33\", \"not-a-cidr\", \"10.0.0.x\"]) and calling the config load path that invokes trustedProxies(). Any string that netip.ParsePrefix rejects (bad bits value, bare IP without /mask, garbage) triggers it.","commonSituations":"Copy-pasting a reverse-proxy IP without the CIDR suffix (e.g. \"10.0.0.1\" instead of \"10.0.0.1/32\"), typos, or leftover YAML quoting artifacts. Common after adding an nginx/traefik load balancer in front of headscale.","solutions":["Correct the offending entry to a valid CIDR prefix, e.g. 10.0.0.1/32 or 172.16.0.0/12","Make sure every list element is quoted consistently in YAML/JSON and has no trailing spaces","Leave trusted_proxies unset if you do not sit behind a proxy — the empty list returns nil, nil"],"exampleFix":"# before\ntrusted_proxies:\n  - 10.0.0.1\n# after\ntrusted_proxies:\n  - 10.0.0.1/32","handlingStrategy":"validation","validationCode":"// validate before config load / server start\nimport \"net/netip\"\n\nfunc validateTrustedProxies(raw []string) error {\n\tfor i, s := range raw {\n\t\tif _, err := netip.ParsePrefix(s); err != nil {\n\t\t\treturn fmt.Errorf(\"entry %d (%q) is not a valid CIDR: %w\", i, s, err)\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"err := hskinValidate(cfg) // config load\nif err != nil {\n    log.Fatal().Err(err).Msg(\"invalid configuration\")\n}","preventionTips":["Treat trusted_proxies entries as CIDRs, not bare IPs — always include /NN","Run `headscale config validate` (or equivalent load in dry-run) in CI for config files","Use YAML anchors/schema checks to catch unquoted or malformed list entries"],"tags":["config","network","proxy","startup"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}