gofr-dev/gofr · error

unexpected result type from circuit breaker

Error message

unexpected result type from circuit breaker

What it means

ErrUnexpectedCircuitBreakerResultType is returned by (*circuitBreaker).handleCircuitBreakerResult when the value stored in the breaker's result channel is not an *http.Response even though err was nil. It guards the internal goroutine/typed-result contract of the circuit breaker implementation.

Source

Thrown at pkg/gofr/service/circuit_breaker.go:20

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      Metrics
	serviceName  string

View on GitHub (pinned to 187eb24962)

Solutions

  1. Pin/upgrade to a gofr version where the circuit breaker result type is consistent; check the changelog for circuit_breaker.go fixes
  2. Don't call the breaker's internals directly — always go through the service's doRequest/executeWithCircuitBreaker API
  3. If reproducible, file a bug with a minimal reproduction including the CircuitBreakerConfig and call pattern
  4. Wrap the call and use errors.Is(err, ErrUnexpectedCircuitBreakerResultType) to distinguish it from real network failures
Defensive patterns

Strategy: type-guard

Validate before calling

resp, err := svc.doRequest(ctx, req)
if errors.Is(err, ErrUnexpectedCircuitBreakerResultType) {
    // internal breaker contract violation: treat as non-retryable bug
}

Type guard

func isUnexpectedBreakerResult(err error) bool { return errors.Is(err, ErrUnexpectedCircuitBreakerResultType) }

Try / catch

resp, err := svc.Get(ctx, url)
if err != nil {
    if errors.Is(err, ErrUnexpectedCircuitBreakerResultType) {
        log.Error("circuit breaker returned invalid result; report bug")
        return err // do not retry
    }
    return err
}

Prevention

When it happens

Trigger: executeWithCircuitBreaker submits the request to the breaker, whose internal result comes back through an any-typed channel; a failed type assertion result.(*http.Response) with err==nil makes handleCircuitBreakerResult return this error. In practice it indicates an internal bug or misuse of the breaker's execution path rather than a caller mistake.

Common situations: Concurrent modifications or custom code paths feeding unexpected values into the breaker's result; library upgrades that changed the internal result contract; race conditions between the request goroutine and the breaker state machine.

Related errors


AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01). Data as JSON: /api/errors/896769c36d4f5034. Report an issue: GitHub.