From 993861977c3bf23ad1c5ec4aa74c02b6b18d9ffe Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Sat, 22 Aug 2026 13:21:15 -0700 Subject: [PATCH 1/3] Force consistent software.amazon.awssdk version across the build Spring AI's bedrock/bedrock-converse starters (added for BedrockProvider) transitively pull a newer AWS SDK release than awsSdkVersion, which embedded, cloudServices, and professional's own direct AWS SDK dependency already use. AWS SDK v2 requires every software.amazon.awssdk artifact to share one version, so the skew left two copies of classes like software.amazon.awssdk.regions.Region loaded across the embedded/module classloader boundary, surfacing at server startup as a LinkageError (loader constraint violation) rather than a build-time conflict -- this is what's been failing TeamCity's upgrade validation and other suites since the Bedrock dependency landed. --- build.gradle | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/build.gradle b/build.gradle index e82d740d60..1aeb3b192f 100644 --- a/build.gradle +++ b/build.gradle @@ -402,6 +402,17 @@ allprojects { // Spring AI's pgvector-store brings in its own PostgreSQL JDBC driver; force ours force "org.postgresql:postgresql:${postgresqlDriverVersion}" + // professional's spring-ai-bedrock/spring-ai-bedrock-converse pull a newer software.amazon.awssdk + // release than awsSdkVersion (used directly by embedded, cloudServices, and professional itself). + // Every software.amazon.awssdk artifact must be the same version -- that's why AWS publishes an + // SDK-wide BOM -- so a skew here leaves two different copies of classes like + // software.amazon.awssdk.regions.Region loaded across the embedded/module classloader boundary, + // which surfaces at runtime as a LinkageError (loader constraint violation), not a build failure. + eachDependency { DependencyResolveDetails details -> + if (details.requested.group == "software.amazon.awssdk") + details.useVersion(awsSdkVersion) + } + dependencySubstitution { // Because the client api artifact name is not the same as the directory structure, we use // Gradle's dependency substitution so the dependency will appear correctly in the pom files that From d16f645896814f8ae18fb1249f8c2e8dcad68508 Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Sat, 22 Aug 2026 13:47:43 -0700 Subject: [PATCH 2/3] awsSdkVersion=2.41.22 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 9713b4d47d..8b3704dfe9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -105,7 +105,7 @@ apacheTomcatVersion=11.0.24 # tika asmVersion=9.10.1 -awsSdkVersion=2.29.50 +awsSdkVersion=2.41.22 # Microsoft library for sending OAuth2-authenticated notification emails via the Microsoft Graph API azureIdentityVersion=1.18.4 From 30d4807497f6649c22e9d2e263668b7445d5a314 Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Sat, 22 Aug 2026 14:58:20 -0700 Subject: [PATCH 3/3] And share classes with LabKeySpringBootClassLoader --- .../labkey/embedded/LabKeySpringBootClassLoader.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/server/embedded/src/org/labkey/embedded/LabKeySpringBootClassLoader.java b/server/embedded/src/org/labkey/embedded/LabKeySpringBootClassLoader.java index 7e3a4d3f77..64100971f9 100644 --- a/server/embedded/src/org/labkey/embedded/LabKeySpringBootClassLoader.java +++ b/server/embedded/src/org/labkey/embedded/LabKeySpringBootClassLoader.java @@ -102,6 +102,17 @@ protected boolean filter(String name, boolean isClassName) { return true; } + // The AWS SDK is bundled both by the embedded boot classpath (e.g., for the SSM-backed + // application.properties/secrets support) and by modules that call the SDK directly (e.g., + // CloudServices, professional). Independently defining the same classes in both classloaders + // -- even at identical versions -- triggers a loader constraint violation whenever code loaded by + // one side hands an AWS SDK type to code loaded by the other. Deferring here means the module + // classloader reuses whichever copy the parent already loaded, falling back to its own bundled + // jars only for SDK modules (e.g., Bedrock) that the parent doesn't carry. + if (name.startsWith("software.amazon.awssdk.")) + { + return true; + } return super.filter(name, isClassName); }