spring-projects / spring-projects/spring-framework

ContextPropagation does not work for suspend endpoints when coroutine suspension occurs

Open
#37,002 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

in: core status: waiting-for-triage theme: kotlin
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

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.