Making Spring WebClient calls causes RequestContext.current() to throw IllegalStateException
See original GitHub issueWe are running a Spring Boot WebFlux service written in Kotlin with coroutines. The Armeria Spring Boot starter has been used, with Armeria providing the org.springframework.web.reactive.function.client.WebClient implementation.
In any handler function, we’re seeing that making any HTTP call with the Armeria WebClient causes any subsequent calls to RequestContext.current() throw IllegalStateException with "RequestContext unavailable".
We noticed the issue as we have a decorator to pass on some specific request headers from the originating request to our gRPC endpoints. When we added a WebClient call before making the gRPC calls, the decorator was no longer able to grab the headers as the context is not found.
Here’s a minimal service which reproduces the issue. The first log prints the context, the second one throws IllegalStateException.
import com.linecorp.armeria.common.RequestContext
import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.awaitExchange
import org.springframework.web.reactive.function.server.ServerResponse
import org.springframework.web.reactive.function.server.bodyValueAndAwait
import org.springframework.web.reactive.function.server.coRouter
@SpringBootApplication
class ContextBugApplication
fun main(args: Array<String>) {
runApplication<ContextBugApplication>(*args)
}
@Configuration
class ContextClobberingConfiguration(
webClientBuilder: WebClient.Builder
) {
private val webClient: WebClient = webClientBuilder.baseUrl("https://httpstat.us").build()
private val logger = LoggerFactory.getLogger(this::class.java)
@Bean
fun routes() = coRouter {
GET("/").invoke {
RequestContext.current<RequestContext>().also {
logger.debug("Request context: $it") // Request context: [sreqId=..., chanId=..., ...]
}
webClient.get().uri("/200").awaitExchange()
RequestContext.current<RequestContext>().also {
logger.debug("Request context: $it") // throws IllegalStateException
}
ServerResponse.ok().bodyValueAndAwait("ok")
}
}
}
Issue Analytics
- State:
- Created 3 years ago
- Comments:9 (6 by maintainers)

Top Related StackOverflow Question
Hi @minwoox, thanks for the quick response!
I can confirm that when I explicitly use the coroutine dispatcher (by wrapping the handler code with
withContext), I am then able to successfully access the root context before and after making the WebClient call.Is this expected behaviour? We don’t see the same thing when making gRPC calls (using a
FutureStubwith.await()) - the original request context is preserved afterwards.I think you don’t have to. 😄 I guess @ikhoon is just telling an option.
Sorry, we need one. 😅 You might want to refer to a slide though. https://github.com/minwoox/deview2020-requestscoping/blob/master/DEVIEW2020_RequestScoping.pdf