{"record":{"id":"6a7b87ee85767f37","repo":"cloudflare/cloudflared","slug":"expect-to-write-d-bytes-for-rpc-stream-protocol-s-6a7b87","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/session_client.go","lineNumber":31,"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// SessionClient calls capnp rpc methods of SessionManager.\ntype SessionClient struct {\n\tclient         pogs.SessionManager_PogsClient\n\ttransport      rpc.Transport\n\trequestTimeout time.Duration\n}\n\nfunc NewSessionClient(ctx context.Context, stream io.ReadWriteCloser, requestTimeout time.Duration) (*SessionClient, 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\treturn &SessionClient{\n\t\tclient:         pogs.NewSessionManager_PogsClient(conn.Bootstrap(ctx), conn),\n\t\ttransport:      transport,\n\t\trequestTimeout: requestTimeout,\n\t}, nil\n}\n\nfunc (c *SessionClient) 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.SessionManager, metrics.OperationRegisterUdpSession).Inc()\n\ttimer := metrics.NewClientOperationLatencyObserver(metrics.SessionManager, metrics.OperationRegisterUdpSession)\n\tdefer timer.ObserveDuration()\n\n\tresp, err := c.client.RegisterUdpSession(ctx, sessionID, dstIP, dstPort, closeIdleAfterHint, traceContext)","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/tunnelrpc/quic/session_client.go#L13-L49","documentation":"NewSessionClient writes the 6-byte RPC stream protocol signature to the stream as a handshake preamble. This error is returned when io.Writer.Write accepted fewer bytes than the full 6-byte signature (partial write), meaning the handshake preamble was not fully transmitted.","triggerScenarios":"stream.Write(rpcStreamProtocolSignature[:]) returns n < 6 with err == nil — a partial write on the underlying QUIC stream, typically due to the stream being closed/reset concurrently or a short-write-capable writer.","commonSituations":"Underlying QUIC stream reset while the RPC client is being initialized; using a wrapper writer that performs short writes; network teardown racing session setup.","solutions":["Retry or recreate the stream; a partial signature write leaves the handshake unusable","Check whether the stream was closed or reset concurrently during NewSessionClient","Wrap the writer so Write blocks until all bytes are sent (full-write loop) before calling NewSessionClient"],"exampleFix":"// before: raw stream may short-write\nsessionClient, err := NewSessionClient(ctx, stream, timeout)\n// after: ensure full writes first\nfw := &fullWriter{w: stream} // Write loops until all bytes are written\nsessionClient, err := NewSessionClient(ctx, fw, timeout)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"sc, err := NewSessionClient(ctx, stream, timeout)\nif err != nil {\n    if strings.Contains(err.Error(), \"expect to write\") {\n        // partial write: recreate stream and retry once\n        stream.Close()\n        return retryWithNewStream(ctx)\n    }\n    return err\n}","preventionTips":["Do not close or cancel the stream concurrently with NewSessionClient","Use a writer that guarantees full writes (loop until n == len(b))","Check for stream reset errors in surrounding QUIC code before session setup"],"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"}