gofr-dev/gofr · error
unable to connect to server at host
Error message
unable to connect to server at host
What it means
ErrCircuitOpen is the sentinel error returned by the HTTP service's circuit breaker when the circuit is in the OpenState: recent failures exceeded the configured Threshold, so the client short-circuits and refuses to send requests until the reset/health interval elapses. The misleading message ('unable to connect to server at host') actually means the request was blocked locally by the breaker, not attempted and refused.
Source
Thrown at pkg/gofr/service/circuit_breaker.go:19
package service
import (
"context"
"errors"
"net/http"
"sync"
"time"
)
// circuitBreaker states.
const (
ClosedState = iota
OpenState
)
var (
// ErrCircuitOpen indicates that the circuit breaker is open.
ErrCircuitOpen = errors.New("unable to connect to server at host")
ErrUnexpectedCircuitBreakerResultType = errors.New("unexpected result type from circuit breaker")
)
// CircuitBreakerConfig holds the configuration for the circuitBreaker.
type CircuitBreakerConfig struct {
Threshold int // Threshold represents the max no of retry before switching the circuit breaker state.
Interval time.Duration // Interval represents the time interval duration between hitting the HealthURL
}
// circuitBreaker represents a circuit breaker implementation.
type circuitBreaker struct {
mu sync.RWMutex
state int // ClosedState or OpenState
failureCount int
threshold int
interval time.Duration
lastChecked time.Time
metrics MetricsView on GitHub (pinned to 187eb24962)
Solutions
- Check downstream service health and restore it; the breaker will close once the health endpoint succeeds after Interval
- Tune CircuitBreakerConfig: raise Threshold for transient-error tolerance and shorten Interval for faster recovery
- Use errors.Is(err, ErrCircuitOpen) to implement caller-side fallback/caching while the circuit is open
- Monitor/logs the breaker state transitions to find why requests keep failing before the trip
Example fix
// before
cfg := &CircuitBreakerConfig{Threshold: 2, Interval: 5 * time.Minute}
// after (tolerate more failures, recover faster)
cfg := &CircuitBreakerConfig{Threshold: 5, Interval: 30 * time.Second, Timeout: 2 * time.Second, HealthURL: svcURL + "/.well-known/health"} Defensive patterns
Strategy: retry
Validate before calling
cfg := &CircuitBreakerConfig{Threshold: 5, Interval: 30 * time.Second, Timeout: 2 * time.Second, HealthURL: baseURL + "/health"}
if cfg.Threshold <= 0 || cfg.Interval <= 0 {
return errors.New("circuit breaker misconfigured")
}
_ = service.AddOption(cfg) Type guard
func isCircuitOpen(err error) bool { return errors.Is(err, ErrCircuitOpen) } Try / catch
resp, err := svc.Get(ctx, url)
if err != nil {
if errors.Is(err, ErrCircuitOpen) {
return serveFallbackFromCache(ctx) // don't retry immediately
}
return err
} Prevention
- Use errors.Is(err, ErrCircuitOpen) rather than string comparison (message is misleading)
- Monitor downstream health so the breaker closes quickly after recovery
- Tune Threshold/Interval/Timeout to your downstream's failure profile
- Implement a fallback/cached response path for when the circuit is open
- Check the HealthURL actually returns 2xx in the target environment
When it happens
Trigger: Calling doRequest/executeWithCircuitBreaker on an HTTP service (created via NewHTTPService with circuit breaker options) after Threshold consecutive failures have tripped the breaker; the breaker stays open until the Interval passes and a health check (HealthURL) succeeds, moving it to HalfOpen/Closed.
Common situations: A downstream service is down or timing out and keeps failing health checks; Threshold set too low so a brief blip trips the breaker; Interval too long so the circuit stays open and all calls fail fast with this error; integration tests exercising the breaker.
Related errors
- unexpected result type from circuit breaker
- %w: creating index: %w
- %w: deleting index: %w
- %w: executing search: %w
- %w: executing bulk: %w
AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01).
Data as JSON: /api/errors/1cfdb2a9582d6591.
Report an issue: GitHub.