Hey!
Context
I've noticed, that ContextPropagation does not work by default for endpoint market with suspend, when coroutine suspension occurs, for example delay.
I've created new Spring Boot app, added given dependencies and I thought, that it will work out of the box:
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
implementation("io.micrometer:micrometer-tracing")
implementation("io.micrometer:context-propagation")
implementation("org.springframework.boot:spring-boot-starter-opentelemetry")
implementation("org.springframework.boot:spring-boot-starter-aspectj")
For given endpoint traceId is not propagated. Log After delay does not have traceId inside, as a result, endpoint throws status code 500:
@GetMapping("/not-work")
suspend fun notWork(): String {
logger.info("Before delay")
// 2026-07-02T21:00:11.033+02:00 INFO 13795 --- [suspend-context-propagation] [nio-8080-exec-5]
// [76a9324e0135954444f807074b4f6f6b-649f59948d07aa64] c.n.s.DemoController : Before delay
delay(1000.milliseconds)
logger.info("After delay")
// 2026-07-02T21:00:12.035+02:00 INFO 13795 --- [suspend-context-propagation] [DefaultExecutor]
//[ ] c.n.s.DemoController : After delay
return tracer.currentSpan()!!.context().traceId()
}
Versions
Java Version - 25
plugins {
kotlin("jvm") version "2.3.21"
kotlin("plugin.spring") version "2.3.21"
id("org.springframework.boot") version "4.1.0"
id("io.spring.dependency-management") version "1.1.7"
}
How to reproduce
Expected behaviour
- Context-propagation works
- There is maybe some custom param, whatever, which enables context-propagation
Workaround
I can use some workarounds for that issue.
First workaround
I can add withContext(PropagationContextElement()) on the beginning of every endpoint
Second workaround
Given Aspect works without any problems
@Aspect
@Component
class GlobalWebMvcCoroutineContextAspect {
@Around("within(@org.springframework.web.bind.annotation.RestController *)")
fun injectTraceContextGlobally(joinPoint: ProceedingJoinPoint): Any? {
val args = joinPoint.args
if (args.isEmpty() || args.last() !is Continuation<*>) {
return joinPoint.proceed()
}
val originalContinuation = args.last() as Continuation<Any?>
val wrappedContinuation = object : Continuation<Any?> {
override val context: CoroutineContext
get() = originalContinuation.context + PropagationContextElement()
override fun resumeWith(result: Result<Any?>) {
originalContinuation.resumeWith(result)
}
}
args[args.size - 1] = wrappedContinuation
return joinPoint.proceed(args)
}
}
I'm open to any feedback. I can try to fix this, if needed.
Hey!
Context
I've noticed, that
ContextPropagationdoes not work by default for endpoint market withsuspend, when coroutine suspension occurs, for exampledelay.I've created new Spring Boot app, added given dependencies and I thought, that it will work out of the box:
For given endpoint traceId is not propagated. Log
After delaydoes not have traceId inside, as a result, endpoint throws status code 500:Versions
How to reproduce
https://github.com/mateusz-nalepa/suspend-context-propagation
SuspendContextPropagationApplicationcurl localhost:8080/not-workExpected behaviour
Workaround
I can use some workarounds for that issue.
First workaround
I can add
withContext(PropagationContextElement())on the beginning of every endpointSecond workaround
Given Aspect works without any problems
I'm open to any feedback. I can try to fix this, if needed.