AWS SDK Kotlin 으로 S3 GetObject 메소드 예제를 그대로 사용했는데 NoSuchMethodError 가 발생했다.

suspend fun getObjectBytes(bucketName: String, keyName: String, path: String) {
val request = GetObjectRequest {
key = keyName
bucket = bucketName
}
S3Client { region = "us-east-1" }.use { s3 ->
s3.getObject(request) { resp ->
val myFile = File(path)
resp.body?.writeToFile(myFile)
println("Successfully read $keyName from $bucketName")
}
}
}
12:40:12.053 [Test worker @kotlinx.coroutines.test runner#2] ERROR aws.sdk.kotlin.runtime.auth.credentials.SsoTokenProvider -rpc="S3.GetObject" sdkInvocationId="ff9dc240-6f91-416f-b626-bf086b9675a8"- token refresh failed
java.lang.NoSuchMethodError: 'okhttp3.Request$Builder okhttp3.Request$Builder.tag(kotlin.reflect.KClass, java.lang.Object)'
at aws.smithy.kotlin.runtime.http.engine.okhttp.OkHttpUtilsKt.toOkHttpRequest(OkHttpUtils.kt:51)
at aws.smithy.kotlin.runtime.http.engine.okhttp.OkHttpEngine.roundTrip(OkHttpEngine.kt:51)
plugins {
id("org.springframework.boot") version "3.2.5"
id("io.spring.dependency-management") version "1.1.4"
id("com.google.cloud.tools.jib") version "3.4.2"
kotlin("plugin.spring") version "1.9.24"
kotlin("jvm") version "1.9.24"
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
// Error occurs from here
implementation("aws.sdk.kotlin:s3:1.2.15")
implementation("org.slf4j:slf4j-api:2.0.12")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
}
okhttp3 의 Request 클래스 tag 메소드를 찾을 수 없다는 에러다.
'okhttp3.Request$Builder okhttp3.Request$Builder.tag(kotlin.reflect.KClass, java.lang.Object)'
internal fun HttpRequest.toOkHttpRequest(
execContext: ExecutionContext,
callContext: CoroutineContext,
metrics: HttpClientMetrics,
): OkHttpRequest {
val builder = OkHttpRequest.Builder()
// ⚠️ `tag` NoSuchMethod
builder.tag(SdkRequestTag::class, SdkRequestTag(execContext, callContext, metrics))
builder.url(url.toString())
builder.headers(headers.toOkHttpHeaders())
// ⚠️ Error occurs
open fun <T> tag(type: Class<in T>, tag: T?) = apply {
if (tag == null) {
tags.remove(type)
} else {
if (tags.isEmpty()) {
tags = mutableMapOf()
}
tags[type] = type.cast(tag)!! // Force-unwrap due to lack of contracts on Class#cast()
}
}
문제 원인이 된 클래스 파일은 OKHttpUtils.kt 에서 Request.kt 의 tag() 메소드를 찾지 못한다는 메시지로 이렇게 추론할 수 있다.
Request.kt를 정의한 package 버전(ok과 호환되지 않는다.

Gradle Transitive Dependencies
implementation("aws.sdk.kotlin:s3:1.2.15") {
// kotlin sdk 에서 의존하는 okhttp 최소 버전을 5.0 버전대로 변경
constraints {
implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.14") {
because("okhttp3 ~v4 does not support Request builder (kotlin reflect)")
}
}
}
constraints 문으로 최소 의존 버전을 4.12.0 대신 5.0.0-alpha.14 로 변경했다.
❌ 이 방법은 통하지 않는다.
여전히 okhttp:4.12.0 버전을 의존한다.

alpha 버전이기 때문이다.
gradle 은 Transitive dependency resolution 중에는 Stable release 된 버전 중 가장 최신 버전을 우선으로한다.
alpha 버전은 Dependency resolution 과정에서 탈락한다.

4.12.0 이 현재 Stable release 중 가장 최신버전이다.
5.0.x 버전은 alpha 버전으로 stable release 에 해당하지 않아서 탈락된다.
Transitive 가 아닌 직접 사용할 okhttp 의 의존 버전을 명시한다.
implementation("aws.sdk.kotlin:s3:1.2.15") {
constraints {
implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.14") {
because("okhttp3 ~v4 does not support Request builder (kotlin reflect)")
}
}
}
// Directive Dependency
// AWS SDK constraints 와 무관하게 `okhttp3:5.0.0` 버전 사용
implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.14")
✅ 이 방법은 통한다.

Directive Dependency Resolution 에서는 Stable release 여부와 관계 없이 직접 해당 버전을 사용한다.
Transitive , Directive Dependency 가 둘다 있는 경우 Directive 가 우선한다.
implementation("aws.sdk.kotlin:s3:1.2.15") {
// transitive dependency does not ensure unstable release
constraints {
implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.14") {
because("okhttp3 ~v4 does not support Request builder (kotlin reflect)")
}
}
}
// Enforce specific version when to resolve dependencies
configurations.all {
resolutionStrategy.eachDependency {
if (requested.group == "com.squareup.okhttp3" && requested.name == "okhttp") {
useVersion("5.0.0-alpha.14")
because("okhttp3 ~v4 does not support Request builder (kotlin reflect) on AWS SDK")
}
}
}
Transitive Dependency 라 하더라도 사용 버전을 강제로 5.0.0-alpha.14 로 맞춘다.
좀더 복잡한 의존성 설정이 가능하다.
(2) 번이 더 간단하고 명확하다.
(3) 은 Transitive Dependency 까지 복잡하게 설정되어 있을 때 더 정교한 버전 컨트롤이 필요할 때 사용하라.