{"record":{"id":"a00a7fe38ae36c68","repo":"juanfont/headscale","slug":"building-trusted-proxies-middleware-w","errorCode":null,"errorMessage":"building trusted_proxies middleware: %w","messagePattern":"building trusted_proxies middleware: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"hscontrol/app.go","lineNumber":138,"sourceCode":"\t\treturn nil, fmt.Errorf(\"reading or creating Noise protocol private key: %w\", err)\n\t}\n\n\ts, err := state.NewState(cfg)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"init state: %w\", err)\n\t}\n\n\tapp := Headscale{\n\t\tcfg:               cfg,\n\t\tnoisePrivateKey:   noisePrivateKey,\n\t\tclientStreamsOpen: sync.WaitGroup{},\n\t\tstate:             s,\n\t}\n\n\tif len(cfg.TrustedProxies) > 0 {\n\t\tapp.realIPMiddleware, err = trustedProxyRealIP(cfg.TrustedProxies)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"building trusted_proxies middleware: %w\", err)\n\t\t}\n\t}\n\n\t// Initialize ephemeral garbage collector\n\tephemeralGC := db.NewEphemeralGarbageCollector(func(ni types.NodeID) {\n\t\tnode, ok := app.state.GetNodeByID(ni)\n\t\tif !ok {\n\t\t\tlog.Error().Uint64(\"node.id\", ni.Uint64()).Msg(\"ephemeral node deletion failed\")\n\t\t\tlog.Debug().Caller().Uint64(\"node.id\", ni.Uint64()).Msg(\"ephemeral node deletion failed because node not found in NodeStore\")\n\n\t\t\treturn\n\t\t}\n\n\t\tpolicyChanged, err := app.state.DeleteNode(node)\n\t\tif err != nil {\n\t\t\tlog.Error().Err(err).EmbedObject(node).Msg(\"ephemeral node deletion failed\")\n\t\t\treturn\n\t\t}","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/app.go#L120-L156","documentation":"trustedProxyRealIP failed while building the real-IP middleware from cfg.TrustedProxies (hscontrol/realip.go:26). The config value is a []netip.Prefix — the middleware constructor parses/validates each proxy entry as an IP or CIDR prefix and errors when an entry is not a valid netip address or prefix. This only runs when trusted_proxies is non-empty in the config.","triggerScenarios":"Any trusted_proxies entry that netip.ParsePrefix/ParseAddr rejects: bare hostnames ('proxy.internal'), malformed CIDRs ('10.0.0.0/8 '), IPv4 with IPv6 mask notation, stray quotes/whitespace, or trailing commas producing an empty string entry.","commonSituations":"YAML/JSON config copied from nginx docs using hostname-style proxy names; values with surrounding quotes retained from shell examples; a list item left empty by a templating bug; IPv6 addresses missing brackets.","solutions":["Make every trusted_proxies entry a literal IP (100.64.0.1) or CIDR (10.0.0.0/8) — netip does not resolve DNS names","Trim whitespace and remove surrounding quotes from each YAML value","Drop empty list items (check for trailing commas or empty lines in the config list)","Restart headscale after fixing the config"],"exampleFix":"# before\ntrusted_proxies:\n  - \"proxy.internal\"\n  - \"10.0.0.0/8 \"\n\n# after\ntrusted_proxies:\n  - 10.0.0.0/8\n  - 192.168.1.1","handlingStrategy":"validation","validationCode":"for _, p := range cfg.TrustedProxies {\n    if _, err := netip.ParsePrefix(p); err != nil {\n        if _, aerr := netip.ParseAddr(p); aerr != nil {\n            return fmt.Errorf(\"trusted_proxies entry %q is not an IP or CIDR\", p)\n        }\n    }\n}","typeGuard":"func isValidProxyEntry(s string) bool {\n    _, perr := netip.ParsePrefix(s)\n    _, aerr := netip.ParseAddr(s)\n    return perr == nil || aerr == nil\n}","tryCatchPattern":null,"preventionTips":["Use only literal IPs or CIDRs in trusted_proxies — DNS names are never resolved","Lint the config with `headscale configtest` (or equivalent) before deploy","Avoid quoting/whitespace in list items; keep entries single-line scalars"],"tags":["configuration","http-middleware","network","startup","cidr"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}