I’m facing an issue in my Ktor application where the StatusPages plugin is being executed too late in the request pipeline, after the Monitoring intercept. This delay is causing problems with retrieving the response status in the catch and finally blocks of the Monitoring phase.
Here’s a simplified version of my Monitoring phase intercept:
intercept(ApplicationCallPipeline.Monitoring) {
try {
// Some processing here
proceed() // Move to the next phase
} catch (e: Throwable) {
// Exception handling
} finally {
// Attempting to get the response status here
val status = call.response.status()
// Span cleanup
span.end()
}
}
If an exception is thrown during the request processing, it correctly triggers the catch and then enters the finally block. However, the problem is that at this point, call.response.status()
is still null because the response status is not yet set.
The response status is later set by the StatusPages
plugin, which also applies additional error handling. Ideally, I need the StatusPages
plugin to execute before the Monitoring phase intercept, so that I can access the finalized response status in the finally block.
Question
Is there a way to control the order of execution for the StatusPages
plugin relative to the Monitoring intercept? How can I ensure that StatusPages
runs before reaching the catch and finally blocks in my Monitoring intercept?
Any guidance on managing the order of interceptors in Ktor or handling similar cases would be greatly appreciated. Thank you!
You can add a phase that will go before the Setup
phase and intercept it instead of the Monitoring
phase to ensure the response is already sent by the StatusPages
plugin. Also, you need to place the interception code before installing the StatusPages
plugin. Here is an example:
embeddedServer(CIO, port = 8080) {
val beforeSetup = PipelinePhase("beforeSetup")
insertPhaseBefore(ApplicationCallPipeline.Setup, beforeSetup)
intercept(beforeSetup) {
try {
// Some processing here
proceed() // Move to the next phase
} catch (e: Throwable) {
// Exception handling
} finally {
// Attempting to get the response status here
val status = call.response.status()
// Span cleanup
println(status)
}
}
install(StatusPages) {
status(HttpStatusCode.NotFound) { call, _ ->
call.respond(HttpStatusCode.BadRequest)
}
exception<Throwable> { call: ApplicationCall, cause: Throwable ->
call.respondText { cause.message!! }
}
}
routing {
get {
throw RuntimeException("Error")
}
}
}.start(wait = true)