https://kotlinlang.org logo
Join Slack
Powered by
# ktor
  • h

    hallvard

    07/31/2025, 8:40 AM
    Hi, this is about routing. Having spent a few years with the Play framework before moving to kotlin, I have several times found myself longing for the very compact and clear routes definition in Play. I find it gives me a very good overview over a server to see all its endpoints in one place. So I now created a code generator (using KSP) that will take a routes file in the Play framework style and generate code to call my different request handlers: https://github.com/hallyhaa/ktor-routes-codegen. I have discussed this earlier with @Thiago (here). And also think this simple code-generating annotation could be facilitating migrations from Play framework to Ktor (wanted to tip Sergey Mashkov about this, but his account here is deactivated). There is a short example of how to use this here: https://github.com/hallyhaa/routes-file-example Does anyone think this is practical?
    🎉 1
    p
    • 2
    • 6
  • n

    Nathan Fallet

    07/31/2025, 10:50 PM
    Hello! Do we have any plans to resolve this? We are locked with Kotlin 2.1.10 since any higher version of the Kotlin Multiplateform plugin is not compatible with Ktor plugin anymore (because of the application plugin) https://youtrack.jetbrains.com/issue/KTOR-8464 I made a PR on Google Jib a month ago to help, but it was not reviewed yet, and I don’t know how much time it could take if we just wait for it. (I’m willing to contribute to fix it, but if someone knows someone maintaining jib that could help merge and release this would help)
    e
    a
    • 3
    • 5
  • d

    Dumitru Preguza

    08/03/2025, 6:43 PM
    I have this error on iOS client and I'm not sure how to fix it:
    Copy code
    Error Domain=NSURLErrorDomain Code=-1011 "There was a bad response from the server." UserInfo={NSErrorFailingURLStringKey=<ws://test.com/rpc>, NSErrorFailingURLKey=<ws://test.com/rpc>, _NSURLLErrorWebSocketHandshakeFailureReasonKey=0, NSLocalizedDescription=There was a bad response from the server.}
    Thread in Slack Conversation
    a
    a
    • 3
    • 7
  • s

    S.

    08/04/2025, 1:45 PM
    Maybe consider adding a note about setting
    credentials include
    for js clients to the cookies section. it takes some digging on slack to figure out why it doesn't work out of the box (https://github.com/ktorio/ktor/pull/4793, https://youtrack.jetbrains.com/issue/KTOR-539/Ability-to-use-browser-cookie-storage#focus=Comments-27-4775173.0-0)
    j
    a
    p
    • 4
    • 7
  • e

    Emanuele Iannuzzi

    08/05/2025, 10:17 AM
    What would be the best way to handle JWT authentication for WebSocket routes in Ktor (possibly still making use of the io.ktor:ktor-server-auth-jwt plugin, which unfortunately only supports the token being provided in the
    Authorization
    header following the
    Bearer
    scheme)? Header-based auth isn't profitable in the case of WebSockets, as the clients don't usually expose methods to provison such headers during the preflight HTTP request (in accordance to the WS standard)
    a
    • 2
    • 2
  • k

    Kjartan

    08/06/2025, 12:05 PM
    I have a Ktor http endpoint in which I want to trigger some async side effect that I do not want the caller to wait for. When I attended the talk "*Coroutines and Structured Concurrency in Ktor"* by @simon.vergauwen at KotlinConf I saw an example of how to launch a coroutine on the
    call
    property which as far as I understand does what I want:
    Copy code
    call.launch(Dispatchers.IO) {
      runCatching {
        // some "long running" side effect doing updates in the db that should keep running after the response has been sent back to the client.
      }.onFailure { error ->
        // catch to avoid failing the parent coroutine and log properly
      }
    }
    Is this considered "best practice" for my task? What about launching the coroutine in a new scope not related to the call/route scope:
    Copy code
    CoroutineScope(Dispatchers.IO).launch {
      // same as above
    }
    What is considered best practice?
    p
    m
    s
    • 4
    • 7
  • g

    Gustavo Cesário

    08/07/2025, 1:57 AM
    Hey everyone! I'm developing a ktor API that will be stress tested with k6. The main endpoint only receives a request and adds it to a Channel so it can be consumed by another process. During the first stages of the stress test I can see response times of 1ms~2ms. However, in later stages of the stress test the response times quickly rise to 200ms or even 500ms. The amount of total requests is approximately 15k in one minute. I'd really appreciate some insights of what I could investigate to try to understand those latency spikes. Here is some information about the application: • I built a native image with GraalVM • I'm running on very limited resources (0.6 cpu cores and 160MB of ram) • The application is running in a docker-compose environment • "docker stats" shows that the average cpu usage is 20% and memory usage is 50%. • The endpoint is only responsible for deserializing the request and adding it to the Channel.
    Copy code
    post("/payments") {
                try {
                    val request = call.receive<PaymentRequest>()
                    QueueService.addPaymentRequestToQueue(request)
                    call.respond(HttpStatusCode.Accepted)
                } catch (e: Exception) {
                    call.respond(HttpStatusCode.InternalServerError, "Error processing payment request: ${e.message}")
                }
            }
    • I already tried to wrap QueueService.addPaymentRequestToQueue in
    launch {}
    . • There are other coroutines running at the same time in my application. I'm new to coroutines and ktor, so I'm afraid that I might have messed something with the dispatchers and how I'm creating the coroutines. What could I do to investigate what the problem is?
    a
    c
    • 3
    • 5
  • d

    Dzmitry Neviadomski

    08/08/2025, 12:47 PM
    Hi all, I'm trying to find the best practice for uploading large files via
    multipart/form-data
    with
    HttpClient
    without running out of memory. I've summarized all the different methods I could find as of Ktor
    3.2.3
    in the first message in thread. The official documentation primarily highlights the first method (using a
    ByteArray
    ), which seems unsuitable for large files due to its high memory consumption. From the streaming options I've listed, which is considered the most reliable and efficient? Also, would it be helpful to file a documentation issue to add examples for this use case? Any insights would be greatly appreciated. Thanks!
    🧵 1
    a
    • 2
    • 11
  • p

    Pim

    08/08/2025, 4:30 PM
    Hey everyone, I’d like to confirm something on how tailcards work in routing. The examples in the docs: https://ktor.io/docs/server-routing.html#match_url show that the tailcard
    {…}
    always follows a
    /
    (end of a path segment). Is this a requirement or can the tailcard be used anywhere in the path? For example, a route like
    /test{…}
    . I’ve tested this and it works precisely as I want; I just like to confirm that this is also intended and will not be patched later.
    a
    • 2
    • 4
  • t

    Tristan

    08/08/2025, 11:33 PM
    Hey everyone, I'm working on a contribution to the HttpCache plugin for Ktor Client. I want to improve how it handles dropped network connections, which can be common on mobile devices. Right now, if the connection drops while downloading a file, all the bytes that have been downloaded are lost. I've seen that the exception for this is often a
    ClosedByteChannelException
    . I want to add a feature similar to what Cronet offers, where the plugin can save the partially downloaded file and resume the download using Content-Range. This would only work if the server or CDN supports it. I'm not sure how difficult this task will be. My main question is whether it's possible for a plugin to capture this specific type of error. If anyone has insights on this, I'd appreciate the help!
    a
    • 2
    • 1
  • a

    Alex Styl

    08/10/2025, 2:28 PM
    I am getting "Request body length does not match content-length header" when i do a simple .get() request using ktor on nodejs to github (https://api.github.com/repos/composablehorizons/compose-unstyled) I do not understand if this is ktor, or node or github related. anyone got any clues? (i know it's too broad, but no luck with my searching)
    t
    a
    • 3
    • 6
  • m

    mudasar187

    08/13/2025, 8:40 AM
    Hi, we have a couple of ktor applications. Before we didn't get this WARNING message, but as we've bumped up ktor versions along the way we've started getting the following message:
    Copy code
    message: A task raised an exception. Task: io.netty.channel.AbstractChannel$AbstractUnsafe$8@7a8dccce
    
    stacktrace: java.util.concurrent.RejectedExecutionException: event executor terminated at io.netty.util.concurrent.SingleThreadEventExecutor.reject(SingleThreadEventExecutor.java:1005) at io.netty.util.concurrent.SingleThreadEventExecutor.offerTask(SingleThreadEventExecutor.java:388) at io.netty.util.concurrent.SingleThreadEventExecutor.addTask(SingleThreadEventExecutor.java:381) at io.netty.util.concurrent.SingleThreadEventExecutor.execute(SingleThreadEventExecutor.java:907) at io.netty.util.concurrent.SingleThreadEventExecutor.execute0(SingleThreadEventExecutor.java:873) at io.netty.util.concurrent.SingleThreadEventExecutor.execute(SingleThreadEventExecutor.java:863) at io.netty.channel.DefaultChannelPipeline.destroyUp(DefaultChannelPipeline.java:816) at io.netty.channel.DefaultChannelPipeline.destroy(DefaultChannelPipeline.java:801) at io.netty.channel.DefaultChannelPipeline.access$700(DefaultChannelPipeline.java:45) at io.netty.channel.DefaultChannelPipeline$HeadContext.channelUnregistered(DefaultChannelPipeline.java:1411) at io.netty.channel.DefaultChannelPipeline.fireChannelUnregistered(DefaultChannelPipeline.java:780) at io.netty.channel.AbstractChannel$AbstractUnsafe$8.run(AbstractChannel.java:692) at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:148) at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:141) at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:507) at io.netty.channel.SingleThreadIoEventLoop.run(SingleThreadIoEventLoop.java:182) at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:1073) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Thread.java:1583)
    What could be causing this? I'm trying to understand why we didn't get this before but started getting it with the latest version of ktor 3.2.3 We have alarms for our applications and often get this WARNING alarm which is quite annoying. Anything I can do to resolve this?
    a
    • 2
    • 5
  • b

    Bruce Hamilton

    08/14/2025, 12:05 PM
    Hey channel! 👋 We now have a prototype of the OpenAPI spec generation support available for testing! It allows for automatic OpenAPI spec generation for your routes during compilation. This will make it easier to document your APIs, generate client libraries, and collaborate with other developers. If you're interested in trying it out: 1. Check out the template project. This demonstrates how to configure your Gradle with the correct EAP builds and the new DSL. 2. Refer to the KLIP design doc. Feel free to comment! 3. Fill out this short survey. This will help us to move in the right direction. Thanks for your support!
    🎉 6
    🙌 3
    💯 3
    🚀 5
    🙌🏾 1
    K 8
    s
    • 2
    • 5
  • s

    Said Shatila

    08/14/2025, 2:23 PM
    Hey, I am facing an issue where I have multiple url's that I need to switch between depending on the screen so for example -->
    First url --> <https://main-a.com>
    Second url --> <https://main-b.com>
    and I am using Ktor --> I know I can build a new
    Copy code
    url {
    host = <https://main-b.com> 
    encodedPath = v1 
    protocol = URLProtocol.HTTPS
    }
    and I have my default request in my
    networkmodule.kt
    Copy code
    defaultRequest {
    
    param.append("","")
    param.append("","")
    ......
    }
    So I have a bunch of default parameters so let's say I am doing a normal call just adding the endpoint nothing will change but if I created a new urlBuilder all of the params are being override. What could be the solution for this
    a
    • 2
    • 11
  • s

    S.

    08/15/2025, 3:09 PM
    Are preflight OPTIONS requests not handled automatically by a ktor server? both safari and firefox preflights are answered with 403
    ✅ 2
    j
    • 2
    • 9
  • b

    buszi0809

    08/19/2025, 7:45 AM
    Hi, why default request builder lambda is not
    suspend
    in the Default Request plugin? I want to add some header to the request from a local storage, and the query to that storage is based on coroutines. Thought Ktor as the superior client would handle that easily. To my suprise this won't compile because the builder is missing the
    suspend
    modifier. Why is that?
    Copy code
    install(DefaultRequest) {
        header("some key", someRepo.getValue())
    }
    Where
    someRepo.getValue()
    is suspend function. Based on implementation of Default Request plugin I've managed to create such a bypass for that when building the HttpClient:
    Copy code
    .apply {
        requestPipeline.intercept(HttpRequestPipeline.Before) {
            header("some key", someRepo.getValue())
        }
    }
    • 1
    • 1
  • c

    Carter

    08/19/2025, 11:50 AM
    Ktor 3.1.0 release notes say “Support CIO server on WasmJS and JS targets”. Is there an example of this working?
    a
    • 2
    • 3
  • u

    ursus

    08/19/2025, 7:12 PM
    When using
    testApplication
    , should I wrap it in
    runTest { .. }
    or not? Both seem to work
    Copy code
    @Test fun foo() = runTest {
    	testApplication {
    		//
    	}
    }
    vs
    Copy code
    @Test fun foo() = testApplication {
    	//
    }
    e
    h
    • 3
    • 6
  • d

    Daniel Pitts

    08/19/2025, 10:23 PM
    I'm getting a really strange exception using ktor-client
    3.2.3
    with SSE.
    Copy code
    Caused by: kotlinx.serialization.SerializationException: Serializer for class 'ClientSSESession' is not found.
    For some reason, the
    private suspend inline fun <reified T> HttpClient.processSession
    method is trying to create a body of type
    ClientSSESession
    , which seems wrong to me, but i don't really know.
    Copy code
    statement.body<T, Unit> { session ->
        sessionDeferred.complete(session)
    }
    a
    • 2
    • 6
  • r

    Ryan Woodcock

    08/20/2025, 6:36 AM
    Hey, wondering if anyone can help with an issue I'm facing. Currently when I use ktor wasmJs client to call my ktor webserver with authentication and krpc, the websocket doesn't attach the authorization headers so the request it rejected, but this doesn't cause the request to retry as the js error event is just thrown by ktor. The authentication module also doesn't seem to be adding the authorization header at all when the host domain matches based on the chrome console. Any ideas what might be wrong?
    c
    a
    • 3
    • 6
  • l

    Lukas Anda

    08/20/2025, 10:31 AM
    Hey guys, is there any way to create a ktor serializer (or tweak settings of it) so that it would handle
    "null"
    as null value? This may come for any object not only strings. I was thinking about a custom serializer but I can't find a proper guide for this
    a
    • 2
    • 1
  • m

    Mario Palka

    08/20/2025, 12:17 PM
    Hi 👋 does anybody have working example of Ktor application, which development is through Docker (Compose) containers? I try to make it on my own, but failing with basic stuff like Auto Reload and Debug connection to IDE (JetBrains Idea Ultimate but later want extend to VScode)
    • 1
    • 1
  • s

    ShiinaKin

    08/20/2025, 1:08 PM
    Hey guys, are there any way to catch HttpRequestTimeoutException in a custom plugin? I tried to catch it in Send hook and onRequest hook, but it didn't work. I use ktor in KMP project to provide a apiclient for a swift project, so i need to let caller exec these exception.
    Copy code
    data class NetworkError(
        val type: NetworkErrorType,
        val originalException: Throwable? = null
    )
    
    class NetworkHandleConfig {
        lateinit var onNetworkError: ((NetworkError) -> Unit)
    }
    
    val NetworkHandle = createClientPlugin("NetworkHandle", createConfiguration = ::NetworkHandleConfig) {
        val onNetworkError = pluginConfig.onNetworkError
    
        on(Send) { context ->
            try {
                proceed(context)
            } catch (e: CancellationException) {
                throw e
            } catch (e: HttpRequestTimeoutException) {
                val networkError = NetworkError(
                    type = NetworkErrorType.TIMEOUT_ERROR,
                    originalException = e
                )
                onNetworkError(networkError)
                context.executionContext.cancel(CancellationException("Request cancelled due to timeout"))
                proceed(context)
            } catch (e: Exception) {
                val networkError = when {
                    e.message?.contains("offline", ignoreCase = true) == true ||
                            e.message?.contains("connection", ignoreCase = true) == true ||
                            e.message?.contains("unreachable", ignoreCase = true) == true -> {
                        NetworkError(
                            type = NetworkErrorType.CONNECTION_ERROR,
                            originalException = e
                        )
                    }
    
                    e.message?.contains("timeout", ignoreCase = true) == true -> {
                        NetworkError(
                            type = NetworkErrorType.TIMEOUT_ERROR,
                            originalException = e
                        )
                    }
    
                    e.message?.contains("dns", ignoreCase = true) == true ||
                            e.message?.contains("host", ignoreCase = true) == true -> {
                        NetworkError(
                            type = NetworkErrorType.DNS_ERROR,
                            originalException = e
                        )
                    }
    
                    e.message?.contains("ssl", ignoreCase = true) == true ||
                            e.message?.contains("tls", ignoreCase = true) == true ||
                            e.message?.contains("certificate", ignoreCase = true) == true -> {
                        NetworkError(
                            type = NetworkErrorType.SSL_ERROR,
                            originalException = e
                        )
                    }
    
                    else -> {
                        NetworkError(
                            type = NetworkErrorType.UNKNOWN_ERROR,
                            originalException = e
                        )
                    }
                }
    
                onNetworkError(networkError)
                context.executionContext.cancel(CancellationException("Request cancelled due to network error"))
                proceed(context)
            }
        }
        onRequest { request, _ ->
            request.executionContext.invokeOnCompletion { cause ->
                if (cause?.cause is HttpRequestTimeoutException) {
                    val networkError = NetworkError(
                        type = NetworkErrorType.TIMEOUT_ERROR,
                        originalException = cause.cause
                    )
                    onNetworkError(networkError)
                }
            }
        }
    }
    Copy code
    install(HttpTimeout) {
        requestTimeoutMillis = 3000
    }
    a
    • 2
    • 4
  • a

    Anton Yalyshev [JB]

    08/21/2025, 10:22 AM
    Hi channel! 👋 We recently released a prototype of _OpenAPI spec generation support_. Before we move forward, we really need your feedback to make sure this feature works well for your use cases. 👉 Please try it out and fill in this short survey, even if everything worked well for you. Your feedback will directly shape the future of this feature 💜
    K 2
    a
    s
    • 3
    • 4
  • u

    ursus

    08/21/2025, 7:38 PM
    How do I pass my own test dispatcher to the
    testApplication
    at test time?
    Copy code
    class FooTest {
        ...
        private val scheduler = TestCoroutineScheduler()
        private val standardTestDispatcher = StandardTestDispatcher(scheduler)
        private val dispatcherProvider = TestDispatcherProvider(standardTestDispatcher) <---------
        private val appDatabase = InMemoryDatabase(...)
        private val fooDao = FooDao(dispatcherProvider, appDatabase.fooQueries) <-----------
    
        @Test
        fun foo() = testApplication(standardTestDispatcher) { <---------
            turbineScope {
                val fooTurbine = fooDao.foo.testIn(this)
                assertThat(fooTurbine.awaitItem()).isNull()
                fooDao.saveFoo(..)
                assertThat(fooTurbine.awaitItem()).isNotNull()
                fooTurbine.cancelAndEnsureAllEventsConsumed()
            }
        }
    }
    
    class TestDispatcherProvider(standardTestDispatcher: TestDispatcher) : DispatcherProvider {
        override val io: CoroutineDispatcher = standardTestDispatcher
        override val main: CoroutineDispatcher = standardTestDispatcher
        override val default: CoroutineDispatcher = standardTestDispatcher
    }
    crashes with
    Copy code
    java.lang.IllegalStateException: Detected use of different schedulers. If you need to use several test coroutine dispatchers, create one `TestCoroutineScheduler` and pass it to each of them.
            at kotlinx.coroutines.test.TestCoroutineSchedulerKt.checkSchedulerInContext(TestCoroutineScheduler.kt:257)
            at kotlinx.coroutines.test.TestCoroutineScheduler.registerEvent$kotlinx_coroutines_test(TestCoroutineScheduler.kt:68)
            at kotlinx.coroutines.test.StandardTestDispatcherImpl.dispatch(TestCoroutineDispatchers.kt:150)
            at kotlinx.coroutines.internal.DispatchedContinuationKt.safeDispatch(DispatchedContinuation.kt:254)
            at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:318)
            at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:26)
            at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:170)
            at kotlinx.coroutines.BuildersKt.withContext(Unknown Source)
            at io.ktor.server.testing.TestApplicationKt.runTestApplication(TestApplication.kt:534)
            at io.ktor.server.testing.TestApplicationKt$testApplication$1.invokeSuspend(TestApplication.kt:517)
            at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt:42)
            at io.ktor.test.dispatcher.TestCommonKt$runTestWithRealTime$1.invokeSuspend(TestCommon.kt:40)
            at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$2$1$1.invokeSuspend(TestBuilders.kt:317)
            Caused by:
            java.lang.IllegalStateException: Detected use of different schedulers. If you need to use several test coroutine dispatchers, create one `TestCoroutineScheduler` and pass it to each of them.
                at kotlinx.coroutines.test.TestCoroutineSchedulerKt.checkSchedulerInContext(TestCoroutineScheduler.kt:257)
                at kotlinx.coroutines.test.TestCoroutineScheduler.registerEvent$kotlinx_coroutines_test(TestCoroutineScheduler.kt:68)
                at kotlinx.coroutines.test.StandardTestDispatcherImpl.dispatch(TestCoroutineDispatchers.kt:150)
                at kotlinx.coroutines.internal.DispatchedContinuationKt.safeDispatch(DispatchedContinuation.kt:254)
                at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:318)
                at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:26)
                at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:170)
                at kotlinx.coroutines.BuildersKt.withContext(Unknown Source)
                at io.ktor.server.testing.TestApplicationKt.runTestApplication(TestApplication.kt:534)
                at io.ktor.server.testing.TestApplicationKt$testApplication$1.invokeSuspend(TestApplication.kt:517)
                ....
    Such pattern does work with
    runTest(standardTestDispatcher)
    a
    • 2
    • 13
  • j

    Jay

    08/23/2025, 9:49 PM
    is it possible to limit the Cache size when using the Cache client plugin?
    a
    • 2
    • 1
  • m

    Mario Andhika

    08/28/2025, 2:37 AM
    What makes Ktor enjoyable to work with? Don’t know if it’s just me but whenever I switch to working on my Ktor project I’d be like “yay I’m back on Ktor” 😅. For me it’s that Ktor is clean, lean, and performant but at the same time explicit and clear.
    ❤️ 11
  • j

    Jay

    08/28/2025, 6:02 AM
    if i use OkHttp as my client, does it matter if i put my headers in the OkHttpConfig or in the defaultRequest {} block?
    h
    • 2
    • 1
  • a

    albrechtroehm

    08/28/2025, 9:12 AM
    Hey, anyone here maybe uses Selfie for snapshot-test https://github.com/diffplug/selfie and knows how to get around the limitation with coroutines that are in effect when using
    testApplication
    ?
  • d

    Dewan Tawsif

    08/28/2025, 12:58 PM
    Is there a way to force ktor to read from cache and fail if the request is not cached? In OkHttp I can set cache control to
    CacheControl.FORCE_CACHE
    to achieve this behavior.