{"record":{"id":"7f20586b2fe7aac3","repo":"gravitational/teleport","slug":"cannot-route-to-empty-target-host","errorCode":null,"errorMessage":"cannot route to empty target host","messagePattern":"cannot route to empty target host","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"api/utils/route.go","lineNumber":64,"sourceCode":"\t// it will match servers listening on any port.\n\tPort string\n\t// Resolver can be set to override default hostname lookup\n\t// behavior (used in tests).\n\tResolver HostResolver\n\t// CaseInsensitive enabled case insensitive routing when true.\n\tCaseInsensitive bool\n\t// DisableUnqualifiedLookups disables lookups for unqualified hostnames.\n\tDisableUnqualifiedLookups bool\n}\n\n// HostResolver provides an interface matching the net.Resolver.LookupHost method. Typically\n// only used as a means of overriding dns resolution behavior in tests.\ntype HostResolver interface {\n\t// LookupHost performs a hostname lookup.  See net.Resolver.LookupHost for details.\n\tLookupHost(ctx context.Context, host string) (addrs []string, err error)\n}\n\nvar errEmptyHost = errors.New(\"cannot route to empty target host\")\n\n// NewSSHRouteMatcherFromConfig sets up an ssh route matcher from the supplied configuration.\nfunc NewSSHRouteMatcherFromConfig(cfg SSHRouteMatcherConfig) (*SSHRouteMatcher, error) {\n\tif cfg.Host == \"\" {\n\t\treturn nil, trace.Wrap(errEmptyHost)\n\t}\n\n\tif cfg.Resolver == nil {\n\t\tcfg.Resolver = net.DefaultResolver\n\t}\n\n\tm := newSSHRouteMatcher(cfg)\n\treturn &m, nil\n}\n\n// NewSSHRouteMatcher builds a new matcher for ssh routing decisions.\nfunc NewSSHRouteMatcher(host, port string, caseInsensitive bool) SSHRouteMatcher {\n\treturn newSSHRouteMatcher(SSHRouteMatcherConfig{","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/gravitational/teleport/blob/1283425b60ec5f60d509ba4c791183d452923ff7/api/utils/route.go#L46-L82","documentation":"NewSSHRouteMatcherFromConfig requires a target Host in SSHRouteMatcherConfig; if cfg.Host is empty it returns errEmptyHost wrapped. An SSH route matcher must know which host traffic routes to — an empty host would produce a matcher that can never route, so construction fails fast.","triggerScenarios":"Constructing an SSHRouteMatcher via NewSSHRouteMatcherFromConfig with SSHRouteMatcherConfig{Host: \"\"} — e.g. building a dial/route descriptor from a parsed address or server object whose hostname field is missing.","commonSituations":"Proxy code routing agentless SSH sessions where the target node address was not resolved; config files or API payloads omitting the host; parsing failures upstream silently leaving Host empty.","solutions":["Set cfg.Host to the target hostname/IP before calling NewSSHRouteMatcherFromConfig.","Validate the upstream input (address string, server spec) so an empty/failed parse doesn't propagate an empty host into the route config.","Reject requests earlier in the API layer with a clearer user-facing message like \"target host is required\"."],"exampleFix":"// before\nm, err := utils.NewSSHRouteMatcherFromConfig(utils.SSHRouteMatcherConfig{Host: hostFromAddr})\n// after\nif hostFromAddr == \"\" {\n    return trace.BadParameter(\"target host is required\")\n}\nm, err := utils.NewSSHRouteMatcherFromConfig(utils.SSHRouteMatcherConfig{Host: hostFromAddr})","handlingStrategy":"validation","validationCode":"if routeCfg.Host == \"\" { return trace.BadParameter(\"SSH route target host is required\") }","typeGuard":null,"tryCatchPattern":"m, err := utils.NewSSHRouteMatcherFromConfig(cfg)\nif err != nil && strings.Contains(err.Error(), \"empty target host\") {\n    return trace.BadParameter(\"cannot route: target host missing from request\")\n}","preventionTips":["Validate the parsed target address (hostport.Parse etc.) before building route matchers.","Never propagate empty host fields from API payloads; reject them at the handler boundary.","Add unit tests for routing configs with missing host components."],"tags":["routing","ssh","configuration"],"backgroundTag":"missing-required-config-field","analyzedSha":"1283425b60ec5f60d509ba4c791183d452923ff7","analyzedAt":"2026-09-02T04:06:41.601Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}