{"record":{"id":"fcf31db54e997058","repo":"cloudflare/cloudflared","slug":"expect-to-write-d-bytes-for-rpc-stream-protocol-s","errorCode":null,"errorMessage":"expect to write %d bytes for RPC stream protocol signature, wrote %d","messagePattern":"expect to write (.+?) bytes for RPC stream protocol signature, wrote (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tunnelrpc/quic/cloudflared_client.go","lineNumber":32,"sourceCode":"\t\"github.com/cloudflare/cloudflared/tunnelrpc\"\n\t\"github.com/cloudflare/cloudflared/tunnelrpc/metrics\"\n\t\"github.com/cloudflare/cloudflared/tunnelrpc/pogs\"\n)\n\n// CloudflaredClient calls capnp rpc methods of SessionManager and ConfigurationManager.\ntype CloudflaredClient struct {\n\tclient         pogs.CloudflaredServer_PogsClient\n\ttransport      rpc.Transport\n\trequestTimeout time.Duration\n}\n\nfunc NewCloudflaredClient(ctx context.Context, stream io.ReadWriteCloser, requestTimeout time.Duration) (*CloudflaredClient, error) {\n\tn, err := stream.Write(rpcStreamProtocolSignature[:])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif n != len(rpcStreamProtocolSignature) {\n\t\treturn nil, fmt.Errorf(\"expect to write %d bytes for RPC stream protocol signature, wrote %d\", len(rpcStreamProtocolSignature), n)\n\t}\n\ttransport := tunnelrpc.SafeTransport(stream)\n\tconn := tunnelrpc.NewClientConn(transport)\n\tclient := pogs.NewCloudflaredServer_PogsClient(conn.Bootstrap(ctx), conn)\n\treturn &CloudflaredClient{\n\t\tclient:         client,\n\t\ttransport:      transport,\n\t\trequestTimeout: requestTimeout,\n\t}, nil\n}\n\nfunc (c *CloudflaredClient) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeIdleAfterHint time.Duration, traceContext string) (*pogs.RegisterUdpSessionResponse, error) {\n\tctx, cancel := context.WithTimeout(ctx, c.requestTimeout)\n\tdefer cancel()\n\tdefer metrics.CapnpMetrics.ClientOperations.WithLabelValues(metrics.Cloudflared, metrics.OperationRegisterUdpSession).Inc()\n\ttimer := metrics.NewClientOperationLatencyObserver(metrics.Cloudflared, metrics.OperationRegisterUdpSession)\n\tdefer timer.ObserveDuration()\n","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/tunnelrpc/quic/cloudflared_client.go#L14-L50","documentation":"NewCloudflaredClient writes the RPC stream protocol signature bytes to the QUIC stream as a handshake. If the write returns fewer bytes than the 6-byte signature (without an error), the stream is in an inconsistent state and the client aborts with this error.","triggerScenarios":"Calling NewCloudflaredClient where stream.Write(rpcStreamProtocolSignature[:]) returns n < 6 with err == nil — a short write on the underlying stream.","commonSituations":"Stream closed or reset mid-handshake by the peer; transport congestion/cancellation trimming the write; custom/broken io.ReadWriteCloser implementations that don't guarantee full writes.","solutions":["Retry establishing the connection; transient stream resets clear on reconnect.","Wrap the stream with a full-write helper (io.WriteFull-style) or use the standard QUIC stream implementation.","Check for peer disconnects/version mismatch between client and server signatures."],"exampleFix":"// before\nn, err := stream.Write(rpcStreamProtocolSignature[:])\n// after\nn, err := io.WriteString(stream, string(rpcStreamProtocolSignature[:])) // or loop until all bytes written\nif n != len(rpcStreamProtocolSignature) { return nil, fmt.Errorf(...) }","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"client, err := tunnelrpc.NewCloudflaredClient(ctx, stream, timeout)\nif err != nil {\n    if strings.Contains(err.Error(), \"expect to write\") {\n        // stream handshake failed; reconnect and retry once\n        stream, err = dial(ctx)\n        client, err = tunnelrpc.NewCloudflaredClient(ctx, stream, timeout)\n    }\n}","preventionTips":["Use QUIC stream implementations that complete full writes","Retry connection establishment on transient handshake failures","Verify both sides use identical protocol signature constants"],"tags":["quic","rpc","handshake","short-write"],"backgroundTag":"broken-pipe","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}