Budibase/budibase · error
Failed to connect to resolved IP for ${hostname}: unknown ne
Error message
Failed to connect to resolved IP for ${hostname}: unknown network error What it means
fetchWithBlacklist pins the connection to the first DNS-resolved IP (to prevent DNS rebinding). When the underlying fetchFn call to that pinned IP throws, this catch block wraps the error with the hostname being contacted. The 'unknown network error' variant occurs when the thrown value is not an Error instance.
Source
Thrown at packages/backend-core/src/utils/outboundFetch.ts:212
for (let redirects = 0; redirects <= MAX_REDIRECTS; redirects++) {
const pinnedIp = await resolveSafePinnedIp(nextUrl)
let response: TResponse
try {
response = await fetchFn(
nextUrl,
{
...nextRequest,
agent: makePinnedAgent(nextUrl, pinnedIp),
},
pinnedIp
)
} catch (error) {
const hostname = parseUrl(nextUrl).hostname
if (error instanceof Error) {
error.message = `Failed to connect to resolved IP for ${hostname}: ${error.message}`
throw error
}
throw new Error(
`Failed to connect to resolved IP for ${hostname}: unknown network error`
)
}
if (!isRedirect(response.status)) {
return response
}
releaseResponseBody(response)
if (!followRedirects) {
throw new Error("Redirects are not permitted.")
}
if (redirects === MAX_REDIRECTS) {
break
}
View on GitHub (pinned to a81a902e9a)
Solutions
- Read the wrapped original message after the colon — it identifies the real cause (ECONNREFUSED, ETIMEDOUT, CERT_HAS_EXPIRED, etc.).
- Verify the resolved IP is reachable: `curl -v https://<hostname>` or telnet to IP:port from the same host.
- Check whether a proxy is required (HTTPS_PROXY) and whether direct IP egress is allowed by the firewall.
- If using a custom fetchFn, ensure it only throws Error instances so the original message is preserved.
Example fix
// before (custom fetchFn throwing a string)
throw "connection failed"
// after
throw new Error("connection failed") Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight reachability check before the guarded fetch
const parsed = new URL(url)
const { address } = await dns.promises.lookup(parsed.hostname)
const port = parsed.port || (parsed.protocol === "https:" ? 443 : 80)
await new Promise((resolve, reject) => {
const s = net.connect(Number(port), address)
s.setTimeout(5000)
s.once("connect", () => { s.destroy(); resolve(null) })
s.once("error" as symbol, reject)
s.once("timeout", () => { s.destroy(); reject(new Error("timeout")) })
}) Try / catch
try {
return await fetchWithBlacklist(url, req, { fetchFn })
} catch (err) {
const m = err instanceof Error ? err.message : String(err)
if (m.startsWith("Failed to connect to resolved IP")) {
const cause = m.split(": ").slice(2).join(": ") // e.g. ECONNREFUSED
if (/ETIMEDOUT|ECONNRESET/.test(cause)) return withRetry(url, req)
throw new Error(`Upstream unreachable (${cause}) for ${new URL(url).hostname}`)
}
throw err
} Prevention
- Always throw Error instances from custom fetchFn so root causes aren't lost.
- Add health-check probes for critical upstream endpoints.
- Configure sane timeouts on agents; verify firewall/proxy egress rules.
When it happens
Trigger: The TCP/TLS connection to the resolved IP fails: connection refused, timeout, TLS certificate mismatch (because SNI/hostname still applies but IP differs), or fetchFn rejects with a non-Error value (e.g. a string or undefined thrown by a custom fetchFn).
Common situations: Target service is down or firewalled despite resolving; corporate proxies blocking direct IP connections; custom fetchFn implementations that throw strings; stale DNS pointing at a decommissioned IP.
Related errors
- Failed to download asset: ${response.statusText}
- Failed to fetch file from URL: ${response.statusText}
- Redis error: ${err}
- Connection refused when using proxy. Check proxy configurati
- Failed to send request
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/b0e186d912f26566.
Report an issue: GitHub.