ErrLookupBackground articles › ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them

ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them

"Connection refused" (ECONNREFUSED) and its many wrappers — "Could not connect to a Chroma server. Are you sure it is running?", "Unable to connect to the migration source", "Target not connected", "Can't connect to Redis server" — all mean the same thing at the TCP layer: nothing was listening at the address your client dialed, or the network path rejected the connection before an HTTP exchange happened. This article explains what produces these errors, why so many libraries hide the underlying cause behind their own message, and the systematic way to tell a dead service apart from a wrong port, broken DNS, a blocked firewall, or a NAT/hairpinning problem.

Distilled from 93 documented records across 44 repositories.

Background

Connection refused is produced by the lowest layer of the network stack: the client's TCP connect() completes (or fails) before any application protocol runs. The operating system reports ECONNREFUSED when the remote host is reachable but explicitly rejects the connection — typically because no process is listening on that port — and neighboring errnos cover the rest of the family: ENOTFOUND/EAI_AGAIN for DNS resolution, ETIMEDOUT for firewalls that drop packets instead of rejecting them, ECONNRESET for intermediaries that kill established connections, and TLS handshake failures when the connection opens but the certificate exchange fails. Because all of these share the same symptom (your request never got a response), libraries routinely collapse them into one error of their own.

The dominant pattern across this family is a library-specific wrapper that swallows the underlying cause. Chroma re-raises httpx.ConnectError as a plain ValueError asking "Are you sure it is running?"; Label Studio's Redis storage serializer turns any validate_connection() exception — refused, auth failure, or timeout — into one generic DRF ValidationError; Appwrite wraps any failure of a migration provider (REST or Postgres) in a single migration_provider_error; Phalcon rethrows the raw phpredis message verbatim inside ConnectionFailed; OpenCLI's per-service fetch helpers prefix the original error with "<label> request failed:"; and OpenAI Codex's TransportError::Connection strips the URL and preserves the reqwest error only as a source chain. The practical consequence is that the first thing to do with any of these errors is recover the original cause — via error.source()/__cause__, the interpolated message text, or a manual reproduction with curl — because the wrapper rarely tells you whether the problem is DNS, refused, timeout, or TLS.

Several libraries add their own twists worth knowing. Hadoop detects the rare RFC-permitted TCP self-connect (an ephemeral source port equal to the dead destination port on localhost) and deliberately reports it as connection refused, since no daemon could have been listening; it is transient and retryable. Hadoop's Graphite sink additionally gives up silently after five consecutive connect failures, so a fixed endpoint can still mean lost metrics until a restart. Mintplex-Labs anything-llm converts a collector connectivity failure into an HTTP 404 "Not Found", which looks like a missing route but is really an unreachable service. AnythingLLM's sibling pattern appears in Nextcloud AIO, where the domain validation failure names NAT loopback (hairpinning) explicitly: the connection works from outside your network but the container cannot reach its own public IP from inside the LAN. And rustfs treats NotConnected as a retryable signal, re-queueing Kafka sends for a replay worker instead of failing outright.

From the caller's side, these errors share a diagnostic shape: the request failed before any application-level response existed, so retrying with the same inputs will keep failing until the environment changes. That makes them environmental by nature — a stopped container, a typo'd port, a missing firewall rule, a broken proxy variable, an IPv6-only hostname on an IPv4-only host (the classic Supabase db.<ref>.supabase.co case from an Appwrite server), or a service that is still booting. Across the records, the reliable first move is the same: reproduce the exact TCP connection from the same host/network as the failing client with nc -vz <host> <port>, curl -I, redis-cli PING, or a protocol-specific probe, and only then start reading the wrapper's message.

Common causes

What usually fixes it

Go deeper

Documented occurrences

…and 73 more across the corpus — use search.

Honest provenance: generated on 2026-08-29 from AI-assisted analysis of the linked records. See how records are made.