Skip to content

JVM class initialization deadlock in GuardrailContentSource #1856

@Rizzen

Description

@Rizzen

Describe the bug

We encountered a JVM class initialization deadlock in the AWS Bedrock Kotlin SDK related to the GuardrailContentSource model. We had this problem in our unit tests which runs in parallel and a bunch of Bedrock requests being created.

Summary
Concurrent access to GuardrailContentSource and its nested Output object can lead to a JVM class initialization deadlock. This appears to be caused by circular initialization dependencies between the outer sealed class and its nested singleton objects.

Affected Code

public sealed class GuardrailContentSource {
    public abstract val value: String

    public object Input : GuardrailContentSource() { ... }

    public object Output : GuardrailContentSource() { ... }

    public companion object {
        public fun fromValue(value: String): GuardrailContentSource = when (value) {
            "INPUT" -> Input
            "OUTPUT" -> Output
            else -> SdkUnknown(value)
        }

        private val values: List<GuardrailContentSource> = listOf(
            Input,
            Output,
        )
    }
}

Root Cause

  • Kotlin object declarations are compiled into separate JVM classes with their own class initialization locks.
  • The companion object eagerly initializes values, which references both Input and Output.
  • At the same time, fromValue("OUTPUT") or other code paths may directly trigger initialization of Output.

This allows two threads to initialize classes in opposite order:

Thread A

  • Initializes GuardrailContentSource (holds its class init lock)
  • During companion init, tries to initialize Output

Thread B

  • Initializes Output (holds its class init lock)
  • Requires GuardrailContentSource to be initialized first

This results in a classic circular wait:

  • Thread A waits for Output
  • Thread B waits for GuardrailContentSource

Observed Behavior

  • Application hangs indefinitely during concurrent access
  • Thread dump shows threads blocked on class initialization monitors ()

Impact
This can affect any multithreaded application using the Bedrock Kotlin SDK where these model classes are accessed concurrently, especially under high concurrency or during cold start.

Regression Issue

  • Select this option if this issue appears to be a regression.

Expected behavior

We don't have deadlock.

Current behavior

We have deadlock when running tests in a bunch. I assume it also could happen in production services when you run concurrent requests and get unlucky with clinit order. Would be extremely hard to investigate.

Steps to Reproduce

With simple test example

// Mirrors GuardrailContentSource from aws.sdk.kotlin:bedrockruntime.
// Its companion eagerly references subclass singletons — the source of the circular dependency.
private sealed class GuardrailContentSourceSim {
    companion object {
        val values: List<GuardrailContentSourceSim> = listOf(Input, Output)
        fun values(): List<GuardrailContentSourceSim> = values
    }

    object Input : GuardrailContentSourceSim()
    object Output : GuardrailContentSourceSim()
}

@Execution(ExecutionMode.CONCURRENT)
class MethodLevelParallelDeadlockTest {

    // Group 1: access via companion — triggers GuardrailContentSourceSim.<clinit>
    @Test fun testValues1() { assertNotNull(GuardrailContentSourceSim.values()) }
    @Test fun testValues2() { assertNotNull(GuardrailContentSourceSim.values()) }
    @Test fun testValues3() { assertNotNull(GuardrailContentSourceSim.values()) }

    // Group 2: access subclass directly — triggers Output.<clinit>,
    // which requires GuardrailContentSourceSim (its superclass) to be initialized first
    @Test fun testOutput1() { assertNotNull(GuardrailContentSourceSim.Output) }
    @Test fun testOutput2() { assertNotNull(GuardrailContentSourceSim.Output) }
    @Test fun testOutput3() { assertNotNull(GuardrailContentSourceSim.Output) }
}

Possible Solution

No response

Context

We resolved the issue locally by forcing initialization of values before all tests run - https://github.com/JetBrains/koog/pull/1788/changes

AWS SDK for Kotlin version

1.6.17

Platform (JVM/JS/Native)

JVM

Operating system and version

macOS Tahoe 26.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugThis issue is a bug.p2This is a standard priority issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions