{"record":{"id":"12055abafda736ab","repo":"grpc/grpc-go","slug":"credentials-rawconn-is-dispatched-out-of-grpc","errorCode":null,"errorMessage":"credentials: rawConn is dispatched out of gRPC","messagePattern":"credentials: rawConn is dispatched out of gRPC","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"credentials/credentials.go","lineNumber":148,"sourceCode":"}\n\n// AuthorityValidator validates the authority used to override the `:authority`\n// header. This is an optional interface that implementations of AuthInfo can\n// implement if they support per-RPC authority overrides. It is invoked when the\n// application attempts to override the HTTP/2 `:authority` header using the\n// CallAuthority call option.\ntype AuthorityValidator interface {\n\t// ValidateAuthority checks the authority value used to override the\n\t// `:authority` header. The authority parameter is the override value\n\t// provided by the application via the CallAuthority option. This value\n\t// typically corresponds to the server hostname or endpoint the RPC is\n\t// targeting. It returns non-nil error if the validation fails.\n\tValidateAuthority(authority string) error\n}\n\n// ErrConnDispatched indicates that rawConn has been dispatched out of gRPC\n// and the caller should not close rawConn.\nvar ErrConnDispatched = errors.New(\"credentials: rawConn is dispatched out of gRPC\")\n\n// TransportCredentials defines the common interface for all the live gRPC wire\n// protocols and supported transport security protocols (e.g., TLS, SSL).\ntype TransportCredentials interface {\n\t// ClientHandshake does the authentication handshake specified by the\n\t// corresponding authentication protocol on rawConn for clients. It returns\n\t// the authenticated connection and the corresponding auth information\n\t// about the connection.  The auth information should embed CommonAuthInfo\n\t// to return additional information about the credentials. Implementations\n\t// must use the provided context to implement timely cancellation.  gRPC\n\t// will try to reconnect if the error returned is a temporary error\n\t// (io.EOF, context.DeadlineExceeded or err.Temporary() == true).  If the\n\t// returned error is a wrapper error, implementations should make sure that\n\t// the error implements Temporary() to have the correct retry behaviors.\n\t// Additionally, ClientHandshakeInfo data will be available via the context\n\t// passed to this call.\n\t//\n\t// The second argument to this method is the `:authority` header value used","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/credentials/credentials.go#L130-L166","documentation":"ErrConnDispatched is a sentinel error (not a real failure) that a custom TransportCredentials implementation returns from ClientHandshake or ServerHandshake to signal that it has taken ownership of the underlying net.Conn and routed it outside of gRPC (e.g., to a proxy or in-process handler). gRPC checks for this exact error and, when seen, does NOT close the connection and does not treat it as a handshake failure.","triggerScenarios":"A custom TransportCredentials.ServerHandshake or ClientHandshake implementation returns credentials.ErrConnDispatched after handing the raw net.Conn to a non-gRPC consumer (e.g., an HTTP/1 proxy, a TLS-termination sidecar, or an in-process listener). The error then propagates up through transport.NewServerTransport or grpc.Dial's connection setup.","commonSituations":"Developers writing custom proxy or connection-pooling credentials that intercept certain connections. Also seen with the internal proxyattributes package or custom listener wrappers that conditionally route traffic. The error is expected control flow, not a bug — but it surfaces in logs if not handled correctly.","solutions":["If you are the credential author, ensure you return credentials.ErrConnDispatched (not a wrapped variant) so gRPC's exact-equality check (err == credentials.ErrConnDispatched) matches.","If you see this in application-level error handling, treat it as non-fatal: do not retry or close the connection; gRPC already handles it.","Use errors.Is or direct equality only if you are building transport-level code that inspects handshake results."],"exampleFix":"// before\ncustomConn, err := myProxy.Route(rawConn)\nif err != nil { return nil, nil, fmt.Errorf(\"dispatch failed: %w\", err) }\nreturn nil, nil, errors.New(\"dispatched\") // gRPC closes the conn\n// after\ncustomConn, err := myProxy.Route(rawConn)\nif err != nil { return nil, nil, err }\nreturn nil, nil, credentials.ErrConnDispatched // gRPC leaves conn open","handlingStrategy":"validation","validationCode":"// If inspecting handshake results in transport-level code:\nif err == credentials.ErrConnDispatched {\n    // expected: conn was routed elsewhere, do not close\n    return nil\n}","typeGuard":null,"tryCatchPattern":"// This is a sentinel, not a failure. Handle with direct equality:\nif err == credentials.ErrConnDispatched || err == io.EOF {\n    // normal control flow; do not treat as error\n}","preventionTips":["Return the exact sentinel (credentials.ErrConnDispatched) from custom handshakers, not a wrapped error.","Treat this error as info-level control flow in logs.","Never close the connection when this sentinel is returned."],"tags":["go","grpc","credentials","transport","sentinel"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}