{"record":{"id":"92cadef97898393b","repo":"netbirdio/netbird","slug":"target-host-is-required-for-subnet-targets","errorCode":null,"errorMessage":"target host is required for subnet targets","messagePattern":"target host is required for subnet targets","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"management/internals/modules/reverseproxy/service/service.go","lineNumber":1048,"sourceCode":"\n\tif target.TargetId == \"\" {\n\t\treturn errors.New(\"target_id is required for L4 services\")\n\t}\n\t// Cluster targets resolve their upstream host:port from the target's\n\t// own Host/Port fields just like the other L4 types — buildPathMappings\n\t// emits net.JoinHostPort(target.Host, target.Port) for every L4\n\t// target, so allowing port=0 here would let \":0\" reach the proxy.\n\tif target.Port == 0 {\n\t\treturn errors.New(\"target port is required for L4 services\")\n\t}\n\tswitch target.TargetType {\n\tcase TargetTypePeer, TargetTypeHost, TargetTypeDomain:\n\t\tif err := validateDirectUpstreamHost(0, target); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase TargetTypeSubnet:\n\t\tif target.Host == \"\" {\n\t\t\treturn errors.New(\"target host is required for subnet targets\")\n\t\t}\n\tcase TargetTypeCluster:\n\t\t// target_id carries the cluster address; the proxy resolves\n\t\t// the upstream at request time.\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid target_type %q for L4 service\", target.TargetType)\n\t}\n\tif target.Path != nil && *target.Path != \"\" && *target.Path != \"/\" {\n\t\treturn errors.New(\"path is not supported for L4 services\")\n\t}\n\tif target.Options.SessionIdleTimeout < 0 {\n\t\treturn errors.New(\"session_idle_timeout must be positive for L4 services\")\n\t}\n\tif target.Options.RequestTimeout < 0 {\n\t\treturn errors.New(\"request_timeout must be positive for L4 services\")\n\t}\n\tif target.Options.SkipTLSVerify {\n\t\treturn errors.New(\"skip_tls_verify is not supported for L4 services\")","sourceCodeStart":1030,"sourceCodeEnd":1066,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/management/internals/modules/reverseproxy/service/service.go#L1030-L1066","documentation":"Returned by validateL4Target when target_type is \"subnet\" and target.host is empty. Subnet targets reach an address inside a routed network resource, and that address is carried in the host field (plus port); unlike peer targets, nothing overwrites it later, so it must be supplied up front.","triggerScenarios":"An L4 target declared as target_type=subnet with only target_id set (e.g. the subnet resource ID) and no host; templates that leave host empty expecting the proxy to derive it.","commonSituations":"Assuming target_id alone identifies the destination for subnet resources the way it does for peers. Splitting configs where the resource ID was populated by automation but the address field was forgotten.","solutions":["Set host to the IP (or resolvable name) inside the subnet resource, e.g. \"10.20.1.10\".","Keep port set as well - the next check requires it.","If your destination is actually a peer, use target_type \"peer\" with its target_id instead."],"exampleFix":"// before\n{ \"target_type\": \"subnet\", \"target_id\": \"subnet-res-id\", \"port\": 5432 }\n\n// after\n{ \"target_type\": \"subnet\", \"target_id\": \"subnet-res-id\", \"host\": \"10.20.1.10\", \"port\": 5432 }","handlingStrategy":"validation","validationCode":"func checkSubnetHost(t Target) error {\n\tif t.TargetType == \"subnet\" && t.Host == \"\" {\n\t\treturn errors.New(\"host (address inside the subnet) is required for subnet targets\")\n\t}\n\treturn nil\n}","typeGuard":"func hasSubnetHost(t Target) bool {\n\treturn t.TargetType != \"subnet\" || t.Host != \"\"\n}","tryCatchPattern":"if err := svc.Validate(); err != nil {\n\tif strings.Contains(err.Error(), \"target host is required for subnet\") {\n\t\treturn respondBadRequest(errors.New(\"set host to the destination address inside the subnet resource\"))\n\t}\n\treturn respondBadRequest(err)\n}","preventionTips":["For subnet targets always derive host from the resource's address range, not from a peer lookup.","Keep per-target-type field requirements in a checklist next to your config templates.","Prefer netip.ParseAddr on the host client-side to catch typos early."],"tags":["netbird","reverse-proxy","validation","l4","subnet","targets","go"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}