spring-projects / spring-projects/spring-framework
ContextPropagation does not work for suspend endpoints when coroutine suspension occurs
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 60.2k
- Forks
- 38.8k
- Avg merge
- 5d 2h
- Merged PRs (30d)
- 27
Description
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
- Clone repository
https://github.com/mateusz-nalepa/suspend-context-propagation - Run
SuspendContextPropagationApplication - Run
curl localhost:8080/not-work
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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running SuspendContextPropagationApplication from the reproduction repository and calling /not-work with curl, then compare the trace context before and after delay. Investigate how Spring MVC handles suspend endpoints and context propagation across coroutine suspension. Done means the After delay log and returned trace ID retain the request's propagated context without requiring the endpoint workarounds.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, kotlin, spring
- Domain
- backend, observability
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100