{"record":{"id":"6354850d1507ea8a","repo":"ory/hydra","slug":"no-route-to-host","errorCode":null,"errorMessage":"no route to host","messagePattern":"no route to host","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"oryx/ipx/ssrf.go","lineNumber":97,"sourceCode":"\t\t\treturn nil, &net.OpError{\n\t\t\t\tOp:   \"dial\",\n\t\t\t\tNet:  network,\n\t\t\t\tAddr: nil,\n\t\t\t\tErr: &net.DNSError{\n\t\t\t\t\tErr:         \"no such host\",\n\t\t\t\t\tName:        host,\n\t\t\t\t\tServer:      \"\",\n\t\t\t\t\tIsTimeout:   false,\n\t\t\t\t\tIsTemporary: false,\n\t\t\t\t\tIsNotFound:  true,\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t\treturn nil, &net.OpError{\n\t\t\tOp:   \"dial\",\n\t\t\tNet:  network,\n\t\t\tAddr: nil,\n\t\t\tErr:  errors.New(\"no route to host\"),\n\t\t}\n\t}\n}\n","sourceCodeStart":79,"sourceCodeEnd":101,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/oryx/ipx/ssrf.go#L79-L101","documentation":"ipx's SSRF dialing layer fabricates a *net.OpError{Op:\"dial\"} wrapping errors.New(\"no route to host\") when it must abort a connection attempt (e.g. redirect/blocked-dial simulation in ssrf.go). It mimics the classic EHOSTUNREACH network error so standard net.Error handling applies.","triggerScenarios":"Making an HTTP request through the ipx SSRF-guarded dialer when the guard decides the destination is unreachable/not routable under policy — the dial is refused and a synthetic \"no route to host\" *net.OpError is returned instead of opening a socket.","commonSituations":"Requests to hosts whose resolved IPs are blocked by the SSRF allow/deny policy; redirects to disallowed targets where the guarded dialer terminates the connection; tests exercising blocked-dial paths.","solutions":["Treat it like any net.OpError dial failure: check with errors.As(*net.OpError) and inspect Op/Err before deciding it is policy-related.","Review the target URL/IP against the configured SSRF allowlist/denylist and use an approved destination.","Capture redirects client-side (CheckRedirect) and stop following chains that leave the permitted address space.","Distinguish policy blocks from genuine network issues by logging the resolved IP alongside the error."],"exampleFix":"// before\nresp, err := client.Do(req) // err: dial: no route to host\n// after\nvar opErr *net.OpError\nif errors.As(err, &opErr) && opErr.Op == \"dial\" {\n  log.Printf(\"blocked/unreachable dial to %s: %v\", req.URL.Host, err)\n  return ErrBlockedBySSRFGuard\n}","handlingStrategy":"type-guard","validationCode":"// before requesting, check the target is allowed:\nip, err := netip.ParseAddr(hostOrIP)\nif err != nil { return fmt.Errorf(\"bad host %q\", host) }\nif !ssrfAllowlist.Contains(ip) { return ErrDestinationNotAllowed }","typeGuard":"func isNoRouteToHost(err error) bool {\n  var opErr *net.OpError\n  return errors.As(err, &opErr) && opErr.Op == \"dial\" &&\n    strings.Contains(opErr.Err.Error(), \"no route to host\")\n}","tryCatchPattern":"resp, err := client.Do(req)\nif err != nil {\n  var opErr *net.OpError\n  if errors.As(err, &opErr) && isNoRouteToHost(err) {\n    return ErrBlockedOrUnreachable // treat as policy/routing refusal\n  }\n  return err\n}","preventionTips":["Keep the SSRF allowlist/denylist aligned with the destinations your app calls","Stop redirect following into unapproved address space (custom CheckRedirect)","Log resolved IPs with dial failures to separate policy blocks from network faults","Pre-validate URLs against the policy before issuing requests"],"tags":["network","ssrf","dial","security"],"backgroundTag":"no-route-to-host","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}