Migration to 0.8.0
Edit pageLast modified: 25 June 2025Version 0.8.0 brings a lot of changes, mainly targeted to remove inherently broken functionality and simplify kRPC protocol where possible. This is reflected in the number of breaking changes and deprecations.
This page aims to cover all such changes and associated migration instructions.
Strict mode enforcement
Strict mode is now enforced and can't be disabled. See Strict mode for detailed migrations.
kRPC protocol changes
The following changes are reflected in the kRPC protocol on the wire:
KrpcServerdoesn't send CANCELLATION_ACK messages anymore.KrpcClientsends REQUEST cancellation messages for every individually finished call, canceled or finished successfully
Though changes should not affect most users, for those who like to look at the wire it might be useful to know.
Behavioral changes
Some changes in the behavior of kRPC clients and servers:
Lifetime for client-side streams is changed.
Previously, the stream scopes bounded client-side streams. However, stream scopes are completely removed now, so the client-side streams lifetime is now bound to the request's lifetime. This means that when the function returns, every client stream is closed.
Serial format is now only constructed once per client.
Previously, the serial format was constructed once per RPC call. The serial format can be passed using the
KrpcConfig. And the builder code was executed once per every call.Now this behavior is removed. The serial format is constructed once per client.
Services are now instantiated once per service type (Rpc FQ name) and not once per client-side instance.
Services lost their CoroutineScopes (see Incompatible API changes). That means that there are no individual lifetimes for each service instance now. Instead, now each service stub on a client is merely a proxy for function calls. And on the server side, the service implementation is instantiated once per service type. To control this behavior more granularly on the server, new
deregisterServicefunction is introduced.tip
For kRPC servers, the factory function for service instances is now executed once per service type:
rpcServer.registerService<MyService> { MyServiceImpl() }Handshake is now cold.
Previously, the handshake was executed on client creation. Now, the handshake is executed on the first RPC request.
Incompatible API changes
RpcClient.callServerStreaminglost its default implementation:Before
interface RpcClient {fun <T> callServerStreaming(call: RpcCall): Flow<T> {error("Not implemented")}}After
interface RpcClient {fun <T> callServerStreaming(call: RpcCall): Flow<T>}@Rpcservices lost their CoroutineScope:Before
val service = client.withService<MyService>()assert(service is CoroutineScope) // OKAfter
val service = client.withService<MyService>()assert(service is CoroutineScope) // failRpcClientlost itsCoroutineScope:Before
interface RpcClient : CoroutineScopeAfter
interface RpcClientRpcServerlost itsCoroutineScope:Before
interface RpcServer : CoroutineScopeAfter
interface RpcServerRpcServer.registerServicefactoryparameter changes type from(CoroutineContext) -> Serviceto() -> Service:Before
interface RpcServer {fun <@Rpc Service : Any> registerService(serviceKClass: KClass<Service>,serviceFactory: (CoroutineContext) -> Service,)}inline fun <@Rpc reified Service : Any> RpcServer.registerService(noinline serviceFactory: (CoroutineContext) -> Service,) {registerService(Service::class, serviceFactory)}After
interface RpcServer {fun <@Rpc Service : Any> registerService(serviceKClass: KClass<Service>,serviceFactory: () -> Service,)}inline fun <@Rpc reified Service : Any> RpcServer.registerService(noinline serviceFactory: () -> Service,) {registerService(Service::class, serviceFactory)}RpcServer.registerServicelost itsCoroutineContextparameter:Before
interface RpcServerAfter
interface RpcServer {fun <@Rpc Service : Any> deregisterService(serviceKClass: KClass<Service>,)}RpcCallchanged parameter name and type for function's data:Before
class RpcCall(val descriptor: RpcServiceDescriptor<*>,val callableName: String,val data: Any?,val serviceId: Long,)After
class RpcCall(val descriptor: RpcServiceDescriptor<*>,val callableName: String,val parameters: Array<Any?>,val serviceId: Long,)For Ktor,
HttpClient.rpcextension function is now non-suspendable.KtorRpcClient.webSocketSessionis now wrapped in Deferred:Before
interface KtorRpcClient : RpcClient {val webSocketSession: WebSocketSession}After
interface KtorRpcClient : RpcClient {val webSocketSession: Deferred<WebSocketSession>}KrpcClientabstract class has two new abstract methods:initializeConfigandinitializeTransport. They can be used to delay transport initialization until the first RPC call.To mimic old behavior,
InitializedKrpcClientcan be used:Before
class MyClient(config: KrpcConfig,transport: KrpcTransport,) : KrpcClient(config, transport)After
class MyClient(config: KrpcConfig,transport: KrpcTransport,) : InitializedKrpcClient(config, transport)
API removals
The following APIs are removed:
kotlinx.rpc.RpcClient.callAsync- previously deprecatedkotlinx.rpc.RpcClient.provideStubContextkotlinx.rpc.registerPlainFlowField- previously deprecatedkotlinx.rpc.registerSharedFlowField- previously deprecatedkotlinx.rpc.registerStateFlowField- previously deprecatedkotlinx.rpc.awaitFieldInitialization- previously deprecatedkotlinx.rpc.UninitializedRpcFieldException- previously deprecatedkotlinx.rpc.UninitializedRPCFieldException- previously deprecatedkotlinx.rpc.RpcEagerField- previously deprecatedkotlinx.rpc.RPCCall- previously deprecated aliaskotlinx.rpc.RPCClient- previously deprecated aliaskotlinx.rpc.descriptor.RpcInvokator.Field- previously deprecatedkotlinx.rpc.descriptor.RpcServiceDescriptor.getFields- previously deprecatedkotlinx.rpc.krpc.StreamScope- previously deprecatedkotlinx.rpc.krpc.streamScoped- previously deprecatedkotlinx.rpc.krpc.withStreamScope- previously deprecatedkotlinx.rpc.krpc.invokeOnStreamScopeCompletion- previously deprecatedkotlinx.rpc.krpc.KrpcConfigBuilder.SharedFlowParametersBuilder- previously deprecatedkotlinx.rpc.krpc.KrpcConfigBuilder.sharedFlowParameters- previously deprecatedkotlinx.rpc.krpc.KrpcConfig.sharedFlowBuilder- previously deprecatedkotlinx.rpc.krpc.RPCTransport- previously deprecated aliaskotlinx.rpc.krpc.RPCTransportMessage- previously deprecated aliaskotlinx.rpc.krpc.RPCConfigBuilder- previously deprecated aliaskotlinx.rpc.krpc.client.KRPCClient- previously deprecated aliaskotlinx.rpc.krpc.server.RPCServer- previously deprecated aliaskotlinx.rpc.krpc.server.KRPCServer- previously deprecated aliaskotlinx.rpc.krpc.serialization.RPCSerialFormat- previously deprecated aliaskotlinx.rpc.krpc.serialization.RPCSerialFormatBuilder- previously deprecated aliaskotlinx.rpc.krpc.serialization.RPCSerialFormatConfiguration- previously deprecated aliaskotlinx.rpc.krpc.ktor.client.RPC- previously deprecated aliaskotlinx.rpc.krpc.ktor.client.installRPC- previously deprecated aliaskotlinx.rpc.krpc.ktor.server.RPC- previously deprecated aliaskotlinx.rpc.krpc.ktor.server.RPCRoute- previously deprecated alias
Deprecations
Gradle's
rpc.strictextension is deprecated with an error. Strict mode is now enforced and can't be disabled. See Strict mode for detailed migrations.RemoteServiceis deprecated with error; services are no longer having this interface added during the compilation. See Behavioral changes for services' lifetime information.
Other changes
MISSING_RPC_ANNOTATIONcompiler inspection is removed.ABI incompatible change:
KrpcTransport.receiveCatchingis now an extension function.The following compiler plugin options are removed:
strict-stateFlowstrict-sharedFlowstrict-nested-flowstrict-stream-scopestrict-suspending-server-streamingstrict-not-top-level-server-flowstrict-fields