gofiber/fiber · error

proxy: nil client override passed to Do/Forward

Error message

proxy: nil client override passed to Do/Forward

What it means

Returned by middleware/proxy.selectClient when a per-call client override is passed as the first variadic argument but is nil. Do, Forward, and DomainForward accept an optional *fasthttp.Client override; passing nil explicitly is treated as a programming error (distinct from passing no override at all, which falls back to the global client). The error is unexported, returned from the proxy action.

Solutions

  1. Omit the clients argument entirely to use the global/default client: proxy.Do(c, url).
  2. Ensure any variable passed as the override is non-nil before the call.
  3. If you need a default when your variable is nil, pass nothing rather than nil: wrap with an if.
  4. Set a global client with proxy.WithClient if you want all calls to share one.

Example fix

// before
var cli *fasthttp.Client
proxy.Do(c, url, cli) // nil override -> error
// after
if cli != nil {
    proxy.Do(c, url, cli)
} else {
    proxy.Do(c, url)
}
Defensive patterns

Strategy: validation

Validate before calling

if cli != nil {
    proxy.Do(c, url, cli)
} else {
    proxy.Do(c, url)
}

Try / catch

if err := proxy.Do(c, url, override...); err != nil {
    if errors.Is(err, proxy.ErrNilClientOverride) /* unexported: match by message */ {
        // call again without the nil override
    }
}

Prevention

When it happens

Trigger: Calling proxy.Do(c, url, nil) / proxy.Forward(url, nil) / proxy.DomainForward(host, addr, nil) with a literal nil as the clients variadic. A nil pointer variable passed in the same slot also triggers it.

Common situations: A conditional client variable left nil and forwarded into the call; refactoring that changed a client argument from required to variadic; passing nil to mean 'use default' (the API expects omission for that).

Related errors


AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11). Data as JSON: /api/errors/b3e72598fce9de0d. Report an issue: GitHub.

Appendix: source

Thrown at middleware/proxy/proxy.go:148

				return err
			}
		}

		// Return nil to end proxying if no error
		return nil
	}
}

var defaultClient = &fasthttp.Client{
	NoDefaultUserAgentHeader: true,
	DisablePathNormalizing:   true,
	MaxConnsPerHost:          defaultMaxConnsPerHost,
}

var client atomic.Pointer[fasthttp.Client]

var (
	errNilProxyClientOverride = errors.New("proxy: nil client override passed to Do/Forward")
	errNilGlobalProxyClient   = errors.New("proxy: global client is nil, set a non-nil client with proxy.WithClient")
)

// guardedConfigureClient composes a client's optional pre-existing
// ConfigureClient hook with the dial-time SSRF guard. It is installed on a
// *fasthttp.Client as the bound method value (&guardedConfigureClient{…}).run,
// which fasthttp calls once per HostClient it creates — so the guard is
// present before the first dial to each host and covers both the Dial and
// DialTimeout code paths.
//
// The bound method's code pointer is stable across receivers (unlike a
// closure's), so ensureClientGuarded recognizes an already-guarded client by
// identity — no package-level map keyed by the client, which would pin the
// client and its connection pool for the process lifetime. The struct is
// referenced only from the client's own ConfigureClient field, so it is
// collected together with the client.
type guardedConfigureClient struct {
	// orig is the caller's ConfigureClient hook, or nil. It runs before the

View on GitHub (pinned to a105acad6c)