grpc/grpc-go · info

credentials: rawConn is dispatched out of gRPC

Error message

credentials: rawConn is dispatched out of gRPC

What it means

ErrConnDispatched (credentials/credentials.go:146-148) is a sentinel that a custom TransportCredentials implementation returns to signal that the rawConn has been handed off out of gRPC and the caller MUST NOT close it. It is not a failure — it is a control-flow signal used by proxy/forwarding credentials (e.g. http_proxy credentials) that take ownership of the underlying net.Conn.

Source

Thrown at credentials/credentials.go:148

}

// AuthorityValidator validates the authority used to override the `:authority`
// header. This is an optional interface that implementations of AuthInfo can
// implement if they support per-RPC authority overrides. It is invoked when the
// application attempts to override the HTTP/2 `:authority` header using the
// CallAuthority call option.
type AuthorityValidator interface {
	// ValidateAuthority checks the authority value used to override the
	// `:authority` header. The authority parameter is the override value
	// provided by the application via the CallAuthority option. This value
	// typically corresponds to the server hostname or endpoint the RPC is
	// targeting. It returns non-nil error if the validation fails.
	ValidateAuthority(authority string) error
}

// ErrConnDispatched indicates that rawConn has been dispatched out of gRPC
// and the caller should not close rawConn.
var ErrConnDispatched = errors.New("credentials: rawConn is dispatched out of gRPC")

// TransportCredentials defines the common interface for all the live gRPC wire
// protocols and supported transport security protocols (e.g., TLS, SSL).
type TransportCredentials interface {
	// ClientHandshake does the authentication handshake specified by the
	// corresponding authentication protocol on rawConn for clients. It returns
	// the authenticated connection and the corresponding auth information
	// about the connection.  The auth information should embed CommonAuthInfo
	// to return additional information about the credentials. Implementations
	// must use the provided context to implement timely cancellation.  gRPC
	// will try to reconnect if the error returned is a temporary error
	// (io.EOF, context.DeadlineExceeded or err.Temporary() == true).  If the
	// returned error is a wrapper error, implementations should make sure that
	// the error implements Temporary() to have the correct retry behaviors.
	// Additionally, ClientHandshakeInfo data will be available via the context
	// passed to this call.
	//
	// The second argument to this method is the `:authority` header value used

View on GitHub (pinned to 03255a9237)

Solutions

  1. Treat ErrConnDispatched as a signal, not an error: the conn is now owned elsewhere — do not log/fail on it.
  2. When implementing such a credential, ensure you actually transfer ownership (start a reader/writer goroutine) before returning the sentinel, or you will leak the conn.
  3. If you did not intend to dispatch, return the real handshake error instead so gRPC closes rawConn.
  4. Unit-test that your credential returns this sentinel only on the intended code path.

Example fix

// before — custom cred hands off conn but returns a generic error (conn gets closed)
func (c *proxyCreds) ClientHandshake(ctx context.Context, a string, raw net.Conn) (net.Conn, credentials.AuthInfo, error) {
    go forward(raw)
    return nil, nil, errors.New("dispatched") // gRPC closes raw -> broken
}

// after — return the sentinel so gRPC leaves rawConn alone
func (c *proxyCreds) ClientHandshake(ctx context.Context, a string, raw net.Conn) (net.Conn, credentials.AuthInfo, error) {
    go forward(raw)
    return nil, nil, credentials.ErrConnDispatched
}
Defensive patterns

Strategy: try-catch

Validate before calling

// When consuming a custom credential that may dispatch the conn,
// treat the sentinel as a signal, not an error
func dialWithProxy(target string) error {
    _, _, err := creds.ClientHandshake(ctx, target, raw)
    if errors.Is(err, credentials.ErrConnDispatched) {
        return nil // conn ownership transferred; success
    }
    return err
}

Type guard

func isConnDispatched(err error) bool {
    return errors.Is(err, credentials.ErrConnDispatched)
}

Try / catch

if errors.Is(err, credentials.ErrConnDispatched) {
    // not a failure: rawConn is owned elsewhere; do not close it
}

Prevention

When it happens

Trigger: A custom ClientHandshake/ServerHandshake returns (nil, nil, credentials.ErrConnDispatched) after forwarding/spawning a listener on rawConn. gRPC's transport code treats this sentinel specially to skip closing the connection it no longer owns.

Common situations: Implementing proxy-connect or in-process listener credentials (e.g. the gRPC xDS/proxy code paths); test doubles that move the conn; an accidentally-returned ErrConnDispatched from a buggy custom cred that then leaks the conn.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/12055abafda736ab. Report an issue: GitHub.