ErrLookup › Background 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
- The target service is down or still starting. The single most common trigger across the records: the server process is stopped, crashed, or simply not finished booting when the client connects (Chroma, Redis, beanstalkd, Qdrant, the anything-llm collector, a local Ollama/vLLM backend). Container restart policies and startup ordering (health checks, depends_on, readiness probes) address this directly.
- Wrong host, port, or path in the client configuration. Typo'd hostnames, the wrong port (Hadoop's datanode data port 9866 vs IPC port 9867; Qdrant's REST port 6333; Graphite's 2003), or 'localhost' used from inside a container where the service lives elsewhere. Deriving host/port from one validated config source prevents the drift.
- DNS resolution failure. ENOTFOUND or getaddrinfo failures surface inside the same wrappers: an unresolvable hostname, a broken resolver, or a AAAA-only record on an IPv4-only client (the Supabase direct-connection case). Libraries like Chroma and zeroclaw report DNS errors as connect errors because httpx/reqwest fold them together.
- Firewall, security group, or NAT blocking the connection. Dropped SYNs produce timeouts; rejected connections produce refused; NAT loopback (hairpinning) failures make a service reachable from the internet but not from inside its own LAN — the exact case Nextcloud AIO's domain validation detects. Outbound rules matter as much as inbound ones (Appwrite's egress to migration sources).
- Proxy misconfiguration. Dead or wrong HTTP(S)_PROXY / HTTPS_PROXY settings, proxies that block the target, and the Node-specific gotcha that native fetch ignores HTTPS_PROXY entirely (requiring an undici ProxyAgent, as upstash/context7 documents). NO_PROXY=localhost is also needed when a system proxy intercepts loopback traffic.
- TLS handshake and certificate failures. TLS-intercepting corporate proxies, self-signed chains, and missing enterprise root CAs make the connection fail during the handshake, which many wrappers report as a connection failure. The safe fix is installing the CA (NODE_EXTRA_CA_CERTS, custom-CA transports) rather than disabling verification.
- Port not forwarded or service bound elsewhere. In Docker/Kubernetes the port is not published, the container address differs from what the client uses, or the service binds a different interface. Nextcloud AIO requires 443/tcp forwarded and the apache container actually listening before domain validation can pass.
- Auth or transport confusion at the same address. Some wrappers collapse authentication failures (bad API keys, wrong passwords, Kerberos mismatches) into the same connectivity-looking error — storm's Qdrant manager and Label Studio's Redis validator both do this. A successful TCP probe with correct credentials from the same host distinguishes a network problem from a credentials problem.
What usually fixes it
- [object Object]
- [object Object]
- [object Object]
- [object Object]
- [object Object]
Go deeper
- Connection failures: ECONNREFUSED, ECONNRESET, and friends — why connections get refused, reset, or dropped.
- DNS resolution errors: ENOTFOUND and getaddrinfo failures — how hostname lookups fail and how to debug them.
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Documented occurrences
- Target not connected (rustfs/rustfs)
- Localhost targeted connection resulted in a loopback. No daemon is listening on the target port. (apache/hadoop)
- Not Found (Mintplex-Labs/anything-llm)
- The domain is not reachable on Port 443 from within this container. Have you opened port 443/tcp in your router/firewall? If yes is the problem most likely that the router or firewall forbids local access to your domain. Or in other words: NAT loopback (Hairpinning) does not seem to work in your network. You can work around that by setting up a local DNS server and utilizing Split-Brain-DNS and configuring the daemon.json file of your docker daemon to use the local DNS server. (nextcloud/all-in-one)
- The url supplied '#{response.request.url}' seems to be down (#{response.return_message}) (wpscanteam/wpscan)
- Error creating connection, {}:{} (apache/hadoop)
- describeConnectionError(error, url) (upstash/context7)
- migration_provider_error: Unable to connect to the migration source. Please verify your credentials and ensure the source is reachable from this server. Check for network restrictions such as firewalls, IP allowlists, or outbound connectivity limits. (appwrite/appwrite)
- PROVIDER_UNAVAILABLE: Provider custom is unavailable (ruvnet/ruflo)
- Failed to connect to chromadb. Make sure your server is running and try again. If you are running from a browser, make sure that your chromadb instance is configured to allow requests from the current origin using the CHROMA_SERVER_CORS_ALLOW_ORIGINS environment variable. (chroma-core/chroma)
- migration_provider_error: Unable to connect to the migration source. Please verify your credentials and ensure the source is reachable from this server. Check for network restrictions such as firewalls, IP allowlists, or outbound connectivity limits. (appwrite/appwrite)
- Datanode unreachable. {} (apache/hadoop)
- Access '{access}' does not exist in component '{componentName}' (phalcon/cphalcon)
- Qdrant connection failed: {e} (zeroclaw-labs/zeroclaw)
- 1point3acres request failed: ${error?.message || error} (jackwener/OpenCLI)
- [impeccable] failed to fetch pending count: (pbakaus/impeccable)
- ${label} request failed: ${err?.message ?? err} (jackwener/OpenCLI)
- {underlying exception message} (phalcon/cphalcon)
- connection failed: {0} (openai/codex)
- ${label} request failed: ${err?.message ?? err} (jackwener/OpenCLI)
…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.