Skip to content

ContextPropagation does not work for suspend endpoints when coroutine suspension occurs #37002

Description

@mateusz-nalepa

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    in: coreIssues in core modules (aop, beans, core, context, expression)status: waiting-for-triageAn issue we've not yet triaged or decided ontheme: kotlinAn issue related to Kotlin support

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions