cloudflare/cloudflared · error

unknown protocol %v

Error message

unknown protocol %v

What it means

The RPC session server (Serve) reads the stream's 6-byte protocol signature and only accepts the RPC stream signature; a data-stream signature yields errDataStreamNotSupported and anything else yields this error. It means the server received a stream preamble it does not recognize as a supported protocol.

Source

Thrown at tunnelrpc/quic/session_server.go:37

func NewSessionManagerServer(sessionManager pogs.SessionManager, responseTimeout time.Duration) *SessionManagerServer {
	return &SessionManagerServer{
		sessionManager:  sessionManager,
		responseTimeout: responseTimeout,
	}
}

func (s *SessionManagerServer) Serve(ctx context.Context, stream io.ReadWriteCloser) error {
	signature, err := determineProtocol(stream)
	if err != nil {
		return err
	}
	switch signature {
	case rpcStreamProtocolSignature:
		break
	case dataStreamProtocolSignature:
		return errDataStreamNotSupported
	default:
		return fmt.Errorf("unknown protocol %v", signature)
	}

	// Every new quic.Stream request aligns to a new RPC request, this is why there is a timeout for the server-side
	// of the RPC request.
	ctx, cancel := context.WithTimeout(ctx, s.responseTimeout)
	defer cancel()

	transport := tunnelrpc.SafeTransport(stream)
	defer transport.Close()

	main := pogs.SessionManager_ServerToClient(s.sessionManager)
	rpcConn := tunnelrpc.NewServerConn(transport, main.Client)
	defer rpcConn.Close()

	select {
	case <-rpcConn.Done():
		return nil
	case <-ctx.Done():

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Ensure the client writes rpcStreamProtocolSignature as the first 6 bytes when opening an RPC stream
  2. Align client and server cloudflared versions
  3. Inspect the %v value in the message to see the actual bytes received
  4. If the client intended a data stream, use the corresponding data-stream serving path instead

Example fix

// before: client writes data preamble to an RPC-serving stream
writeDataStreamPreamble(stream)
// after: write the RPC signature the server expects
if err := writeSignature(stream, rpcStreamProtocolSignature); err != nil {
    return err
}
Defensive patterns

Strategy: try-catch

Try / catch

err := server.Serve(ctx, stream)
if err != nil {
    if strings.Contains(err.Error(), "unknown protocol") {
        // close stream; peer sent an unrecognized preamble
        stream.Close()
        return errUnsupportedPeerProtocol
    }
    return err
}

Prevention

When it happens

Trigger: A peer writes neither rpcStreamProtocolSignature nor dataStreamProtocolSignature as its first 6 bytes before the server calls Serve — e.g. a different cloudflared version, a hand-rolled client, or garbage bytes on the stream.

Common situations: Version skew between client and server where the signature scheme changed; a client sending payload data directly without the preamble; corruption of the first bytes on the stream.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/9c03483d5b866a12. Report an issue: GitHub.