cloudflare/cloudflared · error

unknown signature %v

Error message

unknown signature %v

What it means

determineProtocol reads the first 6 bytes (magic signature) from a QUIC stream to identify whether the stream carries the data protocol or the RPC protocol. This error is thrown when the byte sequence read from the stream matches neither known signature, meaning the peer wrote an unrecognized preamble or no preamble at all.

Source

Thrown at tunnelrpc/quic/protocol.go:44

	protocolV1 protocolVersion = "01"

	protocolVersionLength = 2
)

// determineProtocol reads the first 6 bytes from the stream to determine which protocol is spoken by the client.
// The protocols are magic byte arrays understood by both sides of the stream.
func determineProtocol(stream io.Reader) (protocolSignature, error) {
	signature, err := readSignature(stream)
	if err != nil {
		return protocolSignature{}, err
	}
	switch signature {
	case dataStreamProtocolSignature:
		return dataStreamProtocolSignature, nil
	case rpcStreamProtocolSignature:
		return rpcStreamProtocolSignature, nil
	default:
		return protocolSignature{}, fmt.Errorf("unknown signature %v", signature)
	}
}

func writeDataStreamPreamble(stream io.Writer) error {
	if err := writeSignature(stream, dataStreamProtocolSignature); err != nil {
		return err
	}

	return writeVersion(stream)
}

func writeVersion(stream io.Writer) error {
	_, err := stream.Write([]byte(protocolV1)[:protocolVersionLength])
	return err
}

func readVersion(stream io.Reader) (string, error) {
	version := make([]byte, protocolVersionLength)

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Ensure both ends run compatible cloudflared versions that write the protocol signature before any other data
  2. Verify the peer writes writeSignature(dataStreamProtocolSignature) or rpcStreamProtocolSignature as the very first 6 bytes on the stream
  3. Check that no proxy/middleware consumes or rewrites the first bytes of the QUIC stream
  4. Inspect the %v value in the message to identify what the peer actually sent

Example fix

// before: peer writes payload before the signature
stream.Write([]byte(payload))
// after: write the signature first
if err := writeDataStreamPreamble(stream); err != nil {
    return err
}
stream.Write([]byte(payload))
Defensive patterns

Strategy: validation

Validate before calling

// Verify the peer's stream version matches before the handshake
if clientVersion != serverVersion {
    return fmt.Errorf("protocol version mismatch: client %s, server %s", clientVersion, serverVersion)
}

Try / catch

if _, err := determineProtocol(stream); err != nil {
    if strings.Contains(err.Error(), "unknown signature") {
        // fall back or close stream and reconnect with a compatible peer
        stream.Close()
        return errIncompatibleProtocol
    }
    return err
}

Prevention

When it happens

Trigger: Calling ReadConnectResponseData (or any code path invoking determineProtocol) on a stream whose peer did not write one of the two 6-byte magic signatures (0x0A36CD12A13E for data, 0x52BB825CDB65 for RPC) as the first bytes; version mismatch between client and server writing different preambles; garbage or empty stream data.

Common situations: A cloudflared client of a different version connecting to a server expecting this handshake protocol; a stream opened by a non-cloudflared peer; corrupted or truncated QUIC stream data where the first 6 bytes are not the signature; middleware or proxies that consumed/modified the first bytes.

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/a8db40e7c2c85899. Report an issue: GitHub.