https://kotlinlang.org logo
Join SlackCommunities
Powered by
# ktor
  • a

    Arjan van Wieringen

    04/17/2025, 5:31 AM
    Can I create a PR for extending the Ktor Server docs on Routing? Because I am missing docs on typical usecases I find in other server libraries (e.g. ExpressJS etc) on middlewares. Especially adding auth guards on routing parts is pretty important. Having some clear docs on RouteSelector would be great because it is so powerful. And I have seen quite a few errors in blog posts related to auth guards for instance. Some usecases: • transparent routeselectors to show DSL example usages • route guards not only on authentication status
    kodee loving 1
    a
    • 2
    • 2
  • a

    Aidan

    04/18/2025, 6:31 PM
    Hi folks, I understand that the CIO client engine for KTor supports native platforms, however I get this error when I'm using it on iOS (with KMP):
    Copy code
    failed with exception: kotlin.IllegalStateException: TLS sessions are not supported on Native platform.
    Internet searches aren't giving me a ton of good info. Does anyone know if CIO is fully supported or not for iOS?
    r
    s
    • 3
    • 14
  • s

    Sean Chin Jun Kai

    04/19/2025, 6:29 AM
    Hi guys, according to https://github.com/ktorio/ktor/blob/main/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt It says that : Please note that you generally shouldn't use this object directly but use [SSE] plugin with routing builders [sse] instead. Can I ask in what cases should we use this object?
    k
    a
    • 3
    • 20
  • k

    Kuldar

    04/19/2025, 1:12 PM
    I'm currently using ktor 3.0.2 and I am unable to rotate session id. Eg: anon session turns into authenticated session.
    Copy code
    call.sessions.clear<UserSession>()
    call.sessions.set(UserSession(...))
    Any suggestions what should I try next?
    ✅ 1
    • 1
    • 2
  • v

    Ville Orkas

    04/20/2025, 6:48 AM
    hey, I'm getting this weird logging error when a client request fails (I think there is an exception being thrown):
    Copy code
    java.lang.IllegalStateException: No instance for key AttributeKey: CallLogger
    	at io.ktor.util.Attributes$DefaultImpls.get(Attributes.kt:77)
    	at io.ktor.util.AttributesJvmBase.get(AttributesJvm.kt:17)
    	at io.ktor.client.plugins.logging.LoggingKt$Logging$2$4.invokeSuspend(Logging.kt:622)
    	at io.ktor.client.plugins.logging.LoggingKt$Logging$2$4.invoke(Unknown Source:15)
    	at io.ktor.client.plugins.logging.LoggingKt$Logging$2$4.invoke(Unknown Source:6)
    	at io.ktor.client.plugins.logging.ReceiveHook$install$1.invokeSuspend(Logging.kt:759)
    	at io.ktor.client.plugins.logging.ReceiveHook$install$1.invoke(Unknown Source:11)
    	at io.ktor.client.plugins.logging.ReceiveHook$install$1.invoke(Unknown Source:6)
    	at io.ktor.util.pipeline.DebugPipelineContext.proceedLoop(DebugPipelineContext.kt:79)
    	at io.ktor.util.pipeline.DebugPipelineContext.proceed(DebugPipelineContext.kt:57)
    	at io.ktor.client.plugins.ReceiveError$install$1.invokeSuspend(HttpCallValidator.kt:165)
    	at io.ktor.client.plugins.ReceiveError$install$1.invoke(Unknown Source:11)
    	at io.ktor.client.plugins.ReceiveError$install$1.invoke(Unknown Source:6)
    	at io.ktor.util.pipeline.DebugPipelineContext.proceedLoop(DebugPipelineContext.kt:79)
    	at io.ktor.util.pipeline.DebugPipelineContext.proceed(DebugPipelineContext.kt:57)
    	at io.ktor.util.pipeline.DebugPipelineContext.execute$ktor_utils(DebugPipelineContext.kt:63)
    	at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:92)
    	at io.ktor.client.call.HttpClientCall.bodyNullable(HttpClientCall.kt:99)
    I'm using 3.1.2, and I'm not doing anything more than calling GET on an API that returns JSON. Whats going on? I can't find anything on this online
    a
    • 2
    • 3
  • a

    Adam S

    04/20/2025, 8:39 AM
    Question about the multipart form data server docs https://ktor.io/docs/server-requests.html#form_data Isn't it strange that
    var fileDescription
    and
    var fileName
    are outside of the
    post("/upload") { }
    block? What if the form has multiple uploads? Or if the endpoint is called twice simultaneously - one request could overwrite the value from another?
    k
    a
    • 3
    • 2
  • d

    david dereba

    04/21/2025, 6:19 AM
    hello there. Am using Gson and am implementing my own custom serializers specifically for the Instant class but am getting this error. 2025-04-21 091355.340 [eventLoopGroupProxy-4-1] ERROR i.k.server.application.Application - Error occurred: JsonIOException || Failed making field 'java.time.Instant#seconds' accessible; either increase its visibility or write a custom TypeAdapter for its declaring type. See https://github.com/google/gson/blob/main/Troubleshooting.md#reflection-inaccessible com.google.gson.JsonIOException: Failed making field 'java.time.Instant#seconds' accessib. let me share the impl
    Copy code
    package com.root.plugins
    
    
    import com.google.gson.*
    import com.google.gson.FieldNamingPolicy
    import com.google.gson.Strictness
    import com.google.gson.stream.JsonReader
    import com.google.gson.stream.JsonToken
    import com.google.gson.stream.JsonWriter
    import com.root.utility.UtilityFunctions
    import io.ktor.http.ContentType
    import io.ktor.serialization.gson.*
    import io.ktor.serialization.kotlinx.json.json
    import io.ktor.server.application.*
    import io.ktor.server.plugins.contentnegotiation.*
    import io.ktor.server.response.*
    import io.ktor.server.routing.*
    import io.ktor.util.Encoder
    import kotlinx.serialization.ExperimentalSerializationApi
    import kotlinx.serialization.KSerializer
    import kotlinx.serialization.descriptors.PrimitiveKind
    import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
    import kotlinx.serialization.descriptors.SerialDescriptor
    import kotlinx.serialization.encoding.Decoder
    import kotlinx.serialization.json.Json
    import kotlinx.serialization.json.JsonNamingStrategy
    import kotlinx.serialization.modules.SerializersModule
    import java.lang.reflect.Type
    import java.time.Instant
    import kotlinx.datetime.Instant as KtxInstant
    
    @OptIn(ExperimentalSerializationApi::class)
    fun Application.configureSerialization() {
        // Create the fully configured Gson instance
        val gson = GsonBuilder()
            .setPrettyPrinting()
            .setStrictness(Strictness.LENIENT)
            .serializeNulls()
            .registerTypeAdapter(UtilityFunctions.ApiResponseGsonSerializer::class.java, UtilityFunctions.ApiResponseGsonSerializer())
            .registerTypeAdapter(Instant::class.java, object : JsonSerializer<Instant>, JsonDeserializer<Instant> {
                override fun serialize(src: Instant, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
                    return JsonPrimitive(src.toString())
                }
    
                override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Instant {
                    return Instant.parse(json.asString)
                }
            })
            .registerTypeAdapter(Instant::class.java, object : TypeAdapter<Instant>() {
                override fun write(out: JsonWriter, value: Instant?) {
                    if (value == null) {
                        out.nullValue()
                    } else {
                        out.value(value.toString()) // ISO-8601
                    }
                }
    
                override fun read(reader: JsonReader): Instant? {
                    return if (reader.peek() == JsonToken.NULL) {
                        reader.nextNull(); null
                    } else {
                        Instant.parse(reader.nextString())
                    }
                }
            }.nullSafe())
            .registerTypeAdapter(KtxInstant::class.java, object : JsonSerializer<KtxInstant>, JsonDeserializer<KtxInstant> {
                override fun serialize(src: KtxInstant, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
                    return JsonPrimitive(src.toString())
                }
    
                override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): KtxInstant {
                    return KtxInstant.parse(json.asString)
                }
            })
            .setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
            .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
            .create()
    
        install(ContentNegotiation) {
            // Apply the pre-configured Gson instance
                register(ContentType.Application.Json, GsonConverter(gson))
            
    
            json(Json {
                isLenient = true
                prettyPrint = true
                encodeDefaults = true
                ignoreUnknownKeys = true
                coerceInputValues = true
                namingStrategy = JsonNamingStrategy.SnakeCase
                serializersModule = SerializersModule {
                    contextual(KtxInstant::class) {
                        KotlinxInstantSerializer
                    }
                }
            })
        }
    
        routing {
            get("/json/kotlinx-serialization") {
                call.respond(mapOf("hello" to "world"))
            }
            get("/json/gson") {
                call.respond(mapOf("hello" to "world"))
            }
        }
    }
    
    // And define your serializer
    object KotlinxInstantSerializer : KSerializer<KtxInstant> {
        override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("KotlinxInstant", PrimitiveKind.STRING)
    
        override fun serialize(encoder: kotlinx.serialization.encoding.Encoder, value: KtxInstant) {
            encoder.encodeString(value.toString())
        }
    
        override fun deserialize(decoder: Decoder): KtxInstant {
            return KtxInstant.parse(decoder.decodeString())
        }
    }
    then this is the full error log
    Copy code
    2025-04-21 09:13:55.340 [eventLoopGroupProxy-4-1] ERROR i.k.server.application.Application - Error occurred: JsonIOException || Failed making field 'java.time.Instant#seconds' accessible; either increase its visibility or write a custom TypeAdapter for its declaring type.
    See <https://github.com/google/gson/blob/main/Troubleshooting.md#reflection-inaccessible> 
    com.google.gson.JsonIOException: Failed making field 'java.time.Instant#seconds' accessible; either increase its visibility or write a custom TypeAdapter for its declaring type.
    See <https://github.com/google/gson/blob/main/Troubleshooting.md#reflection-inaccessible>
            at com.google.gson.internal.reflect.ReflectionHelper.makeAccessible(ReflectionHelper.java:76)
            at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:388)
            at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:161)
            at com.google.gson.Gson.getAdapter(Gson.java:628)
            at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:201)
            at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:395)
            at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:161)
            at com.google.gson.Gson.getAdapter(Gson.java:628)
            at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:201)
            at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:395)
            at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:161)
            at com.google.gson.Gson.getAdapter(Gson.java:628)
            at com.google.gson.Gson.toJson(Gson.java:928)
            at com.google.gson.Gson.toJsonTree(Gson.java:802)
            at com.google.gson.Gson.toJsonTree(Gson.java:779)
            at com.root.routing.LeasesRoutesKt.handleLeaseCreation(LeasesRoutes.kt:53)
            at com.root.routing.LeasesRoutesKt.access$handleLeaseCreation(LeasesRoutes.kt:1)
            at com.root.routing.LeasesRoutesKt$handleLeaseCreation$1.invokeSuspend(LeasesRoutes.kt)
            at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
            at kotlinx.coroutines.internal.DispatchedContinuation.resumeWith(DispatchedContinuation.kt:197)
            at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SuspendFunctionGun.kt:146)
            at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:120)
            at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(SuspendFunctionGun.kt:11)
            at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(SuspendFunctionGun.kt:70)
            at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
            at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:100)
            at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:173)
            at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:166)
            at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472)
            at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
            at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998)
            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:833)
    Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private final long java.time.Instant.seconds accessible: module java.base does not "opens java.time" to unnamed module @10315254
            at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
            at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
            at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178)
            at java.base/java.lang.reflect.Field.setAccessible(Field.java:172)
            at com.google.gson.internal.reflect.ReflectionHelper.makeAccessible(ReflectionHelper.java:68)
    What could be the issue. please assist.
    🧵 4
    h
    p
    • 3
    • 5
  • y

    Yassine Abou

    04/23/2025, 9:48 PM
    Hey everyone! I’m building a Compose Multiplatform app targeting android/iOS/Desktop Kotlin-Wasm. When calling REST APIs via the Ktor client in the Wasm target, I’m stuck with two errors: 1. *Original Error*`: warning: Node.js net module is not available. Please verify that you are using Node.js` (Happens when using the
    CIO
    engine) 2. After Removing
    CIO
    Engine:`Uncaught runtime errors: ERROR wasm validation error: at offset 5557696: type mismatch: expression has type (ref null 1950) but expected externref` 3. Here is my setup:
    my ktor version is 3.1.0
    and
    compose version is 1.7.3.
    Dependencies (commonMain):
    Copy code
    implementation(libs.ktor.client.core)
    implementation(libs.ktor.client.content.negotiation)
    implementation(libs.ktor.serialization.kotlinx.json)
    implementation(libs.ktor.client.cio)
    Koin DI Configuration:
    Copy code
    single {  Json { ignoreUnknownKeys = true isLenient ; = true encodeDefaults = false } }
    // 2. HTTP Client Configuration 
    single<HttpClient> { HttpClient(CIO) { engine { requestTimeout = 0 } 
    install(ContentNegotiation) { json( json = get(), contentType = ContentType.Application.Json ) } }
    here is the repository link for more context: https://github.com/yassineAbou/LLMS
    a
    u
    • 3
    • 5
  • a

    Austin Pederson

    04/25/2025, 7:48 PM
    Hey all, I'm trying to upload a data class to S3 as JSON using a presigned URL, but I am experiencing issues with the
    ContentNegotiation
    plugin. It looks like the
    ContentNegotiation
    plugin always uses
    Transfer-Encoding: chunked
    which is a problem for S3. This limits me to having to manually serializes this object before uploading it. Is there any way to turn off this behavior? I've scoured the web but found no way to do this as of yet...
    p
    r
    a
    • 4
    • 8
  • k

    Kev

    04/26/2025, 7:50 AM
    Hello. I'm trying to use the routing library but it fails to download. Any ideas?
    Copy code
    Could not find io.ktor:ktor-server-routing:3.1.2.
    Searched in the following locations:
      - <https://repo.maven.apache.org/maven2/io/ktor/ktor-server-routing/3.1.2/ktor-server-routing-3.1.2.pom>
    h
    • 2
    • 1
  • a

    Arjan van Wieringen

    04/27/2025, 7:14 AM
    Is there a reason that
    createApplicationPlugin
    /
    createRouteScopedPlugin
    are typically instantiated as values with a configuration block? In a lot of cases it is much easier to just do:
    Copy code
    fun MyCustomPlugin(val myDeps: MyDeps) = createRouteScopedPlugin("MyCustomPlugin") {
         ...use myDeps
    }
    instead of a configuration class with mutable variables.
    ➕ 2
    s
    • 2
    • 1
  • u

    ursus

    04/29/2025, 2:35 PM
    Copy code
    webSocketSession.send(frame)
    this suspends until it's actually sent? or only until enqueued (and will be sent later)? if it's the later - is there a way to tell it's actually been delivered? -- I need to update my entity's state from
    SENDING
    to
    SENT
    o
    • 2
    • 2
  • c

    Colton Idle

    04/30/2025, 1:19 AM
    Feel like I'm losing my marbles... I have a kmp/cmp app. android/ios/wasm.
    Copy code
    commonMain.dependencies {
                implementation("io.ktor:ktor-client:3.0.0")
    Android works iOS works Running wasm comes with a
    Copy code
    Could not determine the dependencies of task ':kotlinNpmInstall'.
    > Could not resolve all dependencies for configuration ':composeApp:wasmJsNpmAggregated'.
       > Could not resolve io.ktor:ktor-client:3.0.0.
         Required by:
             project :composeApp
          > No matching variant of io.ktor:ktor-client:3.0.0 was found. The consumer was configured to find a library for use during 'kotlin-runtime', preferably optimized for non-jvm, as well as attribute 'org.jetbrains.kotlin.js.public.package.json' with value 'public-package-json', attribute 'org.jetbrains.kotlin.platform.type' with value 'wasm', attribute 'org.jetbrains.kotlin.wasm.target' with value 'js' but:
    u
    c
    a
    • 4
    • 11
  • j

    Jay

    05/01/2025, 10:32 AM
    anyway to enable support for brotli in a client? only have access to gzip and deflate
    Copy code
    install(plugin = ContentEncoding) {
        gzip()
        deflate()
    }
    a
    • 2
    • 1
  • j

    Jay

    05/01/2025, 11:12 AM
    I've also noticed I hit 429 way more often when using OkHttp for my Engine. anyone know why?
    a
    • 2
    • 3
  • u

    ursus

    05/02/2025, 4:09 PM
    If I want websocket to not be active when app goes to background
    Copy code
    appInForeground
        .collectLatest { isInForeground ->
            if (isInForeground) {
                val webSocketSession = ktorClient.webSocketSession(..)
                webSocketSession listenAndSend(webSocketSession)
            }
        }
    is this enough? or does it need cancelling / closing explicitly? Since the WebSocketSession implements CoroutineScope im not sure
    s
    c
    a
    • 4
    • 10
  • m

    Marc

    05/02/2025, 5:47 PM
    did anyone faced this error with the swaggerUI plugin?
    Copy code
    ERROR io.ktor.server.Application - Unhandled: GET - /swagger
    java.lang.ClassCastException: class kotlinx.html.DIV cannot be cast to class kotlinx.html.CoreAttributeGroupFacade (kotlinx.html.DIV and kotlinx.html.CoreAttributeGroupFacade are in unnamed module of loader 'app')
    	at io.ktor.server.plugins.swagger.SwaggerKt$swaggerUI$5$2.invokeSuspend$lambda$9(Swagger.kt:103) [...] etc
    I’m not doing anything misterious, just followed the doccumentation steps 😕
    s
    a
    • 3
    • 28
  • d

    Dejan Lozanovic

    05/04/2025, 1:17 PM
    Is it possible for Ktor Sever and Oauth module to use custom states?
    s
    a
    • 3
    • 3
  • c

    Charann

    05/06/2025, 3:44 AM
    I came across the Ktor collection utils https://api.ktor.io/ktor-utils/io.ktor.util.collections/-concurrent-map/index.html where this thread-safe concurrentMap was marked with "Please do not use it" warning. Is there any specific reason? Because I am in need of a few multiplatform thread-safe collections
    a
    • 2
    • 1
  • e

    Eugen Martynov

    05/06/2025, 7:04 AM
    New member question - can I through monitoring pipeline print server certificate?
    a
    • 2
    • 5
  • a

    arnaud.giuliani

    05/06/2025, 1:34 PM
    Hey all 👋 A question about Ktor plugin registry, does the
    version.ktor.yaml
    file mean that it takes all versions from 2.0 to all superior ones?
    Copy code
    "[2.0,)":
      - io.insert-koin:koin-ktor:$koin
      - io.insert-koin:koin-logger-slf4j:$koin
    ✅ 1
    a
    • 2
    • 2
  • u

    Umesh Solanki

    05/06/2025, 3:50 PM
    Any idea how to upload Blob using
    Copy code
    submitFormWithBinaryData
    I'm using HttpClient(Js)? formData.append didn't work. Thread in Slack Conversation
  • d

    dayanruben

    05/06/2025, 3:52 PM
    Ktor 3.1.3 released https://ktor.io/changelog/3.1/
    🔥 5
    kodee loving 3
    🚀 1
  • h

    Hong Phuc

    05/07/2025, 3:34 AM
    Are all requests coming to an ktor application handled on the same Application's coroutine? Since when I print out the CoroutineContext when a request hit an endpoint I get the same context. Thanks in advance
    s
    e
    • 3
    • 5
  • f

    frevib

    05/07/2025, 12:45 PM
    Hi there, what would be the best approach to run a Kafka consumer (or any message listener) next to Ktor? I know Ktor is a http library only, but most backend developers need to have some Kafka, MQTT or message consumer running next to their HTTP server. For instance, is it recommended to run the Kafka consumer next to the Ktor server, like:
    Copy code
    fun main() = runBlocking {
      launch(<http://Dispatchers.IO|Dispatchers.IO>) {
        embeddedServer(CIO, port = 8080, module = Application::modules)
        .start(wait = true)
      }
    
      launch(<http://Dispatchers.IO|Dispatchers.IO>) {
        startKafkaConsumer()
      }
    }
    or have the Kafka consumer be a module of Ktor, e.g.:
    Copy code
    fun main() {
      embeddedServer(CIO, port = 8080, module = Application::modules)
        .start(wait = true)
    }
    
    fun Application.modules() {
      kafkaModule()
      ktorModules()
    }
    
    fun Application.kafkaModule() {
      launch(<http://Dispatchers.IO|Dispatchers.IO>) {
        startKafkaConsumer()
      }
    }
    Approach 1 seems more architecturally correct, having both Ktor server and Kafka consumer running in a coroutine on the same level. The 2nd approach makes Ktor managing the lifecycle of the Kafka consumer, which is arguably less good.
    a
    • 2
    • 4
  • h

    Hong Phuc

    05/08/2025, 3:10 AM
    Hi all, is the
    httpClient
    thread-safe? From the answer in here and here, I inferred that using
    httpClient
    with the
    OkHttp
    engine is thread-safe. Still can someone confirm if this is true? Thanks in advance Also is the
    httpClient
    supposed to be used as a singleton? Is it possible to change to a specific engine so that I can create multiple instance of
    httpClient
    ?
    👀 1
    a
    • 2
    • 2
  • u

    Ulf

    05/08/2025, 2:33 PM
    Hi all, we're using Ktor client with
    HttpCache
    plugin. We've also made a custom logging plugin based on similar code as in the
    Logging
    plugin. However, one difference from the
    Logging
    plugin is that we would like to log the response even if nothing is sent over the network, ie the cached item is valid and returned directly (see for example
    HttpCache.proceedWithCache
    ). Logging the response body becomes problematic and I'm unsure how to solve that. In the
    Logging
    plugin the
    ResponseObserver
    which is running in the
    receivePipeline
    is used to split the ByteReadChannel that contains the
    rawContent
    so that one is used for logging, but when response comes from cache the
    receivePipeline
    will never run. I can intercept the response in the
    responsePipeline
    though, but am unsure how to handle it here so that i can both log it and have the channel possible to consume later when reading the body, i've tried to something similar with
    split( )
    as in
    ResponseObserver
    but so far failed... (the response body is there but hidden as
    responseBody
    in
    SavedHttpCall
    , so if one could access it directly things would be quite simple...)
    a
    • 2
    • 12
  • h

    hellman

    05/09/2025, 7:33 AM
    We're facing some problems with using Ktor in AWS Fargate behind a load balancer. We want HTTP/2 as the application protocol, but we're getting a Netty exception that looks strange:
    Copy code
    2025-05-09 07:18:11.727 [eventLoopGroupProxy-4-1] TRACE io.ktor.server.Application - Failed to decode request
    java.lang.IllegalArgumentException: text is empty (possibly HTTP/0.9)
    Has anyone seen this and know what we're doing wrong?
    l
    m
    • 3
    • 3
  • h

    hellman

    05/12/2025, 7:13 AM
    Hi! I found a weird behavior with
    sse()
    routes. If we want to send a HTTP response instead of emitting events (e.g.,
    call.respond(HttpStatusCode.BadRequest)
    ), that response code is simply ignored and we always get HTTP 200. Is this an expected behavior? I feel it makes error handling for SSE endpoints difficult.
    a
    • 2
    • 8
  • r

    romainbsl

    05/12/2025, 10:00 AM
    Hi, I have a silly question about intercepting HTTP calls with Ktor client. I see the documentation (https://ktor.io/docs/client-http-send.html) suggest to jump onto HttpSend plugin after configuring the client. But, using a custom plugin (https://ktor.io/docs/client-custom-plugins.html#example-custom-header) seems to lead to the same result using
    onRequest
    . Maybe there is a semantic explanation for both usage, but at the end what do you suggest to use ?
    a
    • 2
    • 4