Skip to content

DynamoDB Enhanced Client Using Wrong Field Name when Querying GSI #6381

@adamsnively

Description

@adamsnively

Describe the bug

Hello, I am running DynamoDbLocal version 3.0.0. I am creating a table in a @SpringBootTest like this:


@TestInstance(TestInstance.Lifecycle.PER_CLASS) // Needed since the beforeAll method needs to be non-static
@SpringBootTest
class DefaultItemServiceTests {

@BeforeAll
void beforeAll() throws Exception {
String port = "8000"
server = ServerRunner.createServerFromCommandLineArgs(
                new String[]{"-inMemory", "-port", port})
server.start()
dbClient = DynamoDbClient.builder().endpointOverride(new URI("http://localhost:8000")).build()
dbClient.createTable(CreateTableRequest.builder()
                .tableName("items")
                .keySchema(List.of(KeySchemaElement.builder().keyType(KeyType.HASH).attributeName("username").build(),
                                   KeySchemaElement.builder().keyType(KeyType.RANGE).attributeName("itemId").build()) as Collection<KeySchemaElement>)
                .globalSecondaryIndexes(GlobalSecondaryIndex.builder().keySchema(
                        List.of(KeySchemaElement.builder().keyType(KeyType.HASH).attributeName("secondaryPartitionKey").build(),
                                KeySchemaElement.builder().keyType(KeyType.RANGE).attributeName("itemId").build()) as Collection<KeySchemaElement>
                ).indexName("items-global-chronological-index").projection(Projection.builder().projectionType(ProjectionType.ALL).build()).build()
                ).attributeDefinitions(
                    List.of(AttributeDefinition.builder().attributeName("username").attributeType(ScalarAttributeType.S).build(),
                            AttributeDefinition.builder().attributeName("itemId").attributeType(ScalarAttributeType.S).build(),
                            AttributeDefinition.builder().attributeName("secondaryPartitionKey").attributeType(ScalarAttributeType.S).build()
                    )
                ).billingMode(BillingMode.PAY_PER_REQUEST)
                .build() as CreateTableRequest
}
}

Regression Issue

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

Expected Behavior

I expected the DynamoDB Enhanced Client to send a Query request like the following:

2025-08-27 18:17:58,098 [main] DEBUG org.apache.http.wire - http-outgoing-3 >> 
"{"TableName":"items","IndexName":"items-global-chronological-index",
"KeyConditionExpression":"#AMZN_MAPPED_secondaryPrimaryKey = :AMZN_MAPPED_secondaryPrimaryKey","ExpressionAttributeNames":{"#AMZN_MAPPED_secondaryPrimaryKey":"secondaryPrimaryKey"},"ExpressionAttributeValues":{":AMZN_MAPPED_secondaryPrimaryKey":{"S":"1"}}}"

Current Behavior

The table appears to be created correctly according to the wire logs:

2025-08-27 18:17:57,638 [main] DEBUG org.apache.http.wire - http-outgoing-2 >> "{"AttributeDefinitions":[{"AttributeName":"username","AttributeType":"S"},{"AttributeName":"itemId","AttributeType":"S"},{"AttributeName":"secondaryPartitionKey","AttributeType":"S"}],"TableName":"items","KeySchema":[{"AttributeName":"username","KeyType":"HASH"},{"AttributeName":"itemId","KeyType":"RANGE"}],"GlobalSecondaryIndexes":[{"IndexName":"items-global-chronological-index","KeySchema":[{"AttributeName":"secondaryPartitionKey","KeyType":"HASH"},{"AttributeName":"itemId","KeyType":"RANGE"}],"Projection":{"ProjectionType":"ALL"}}],"BillingMode":"PAY_PER_REQUEST"}"

However, the DynamoDB Enhanced Client will send a Query request like the following:

2025-08-27 18:17:58,098 [main] DEBUG org.apache.http.wire - http-outgoing-3 >> 
"{"TableName":"items","IndexName":"items-global-chronological-index",
"KeyConditionExpression":"#AMZN_MAPPED_username = :AMZN_MAPPED_username","ExpressionAttributeNames":{"#AMZN_MAPPED_username":"username"},"ExpressionAttributeValues":{":AMZN_MAPPED_username":{"S":"1"}}}"

This query incorrectly references the primary Partition Key instead of the Partition key I configured for that index

Reproduction Steps

Use this model class:

@EqualsAndHashCode
@ToString
@NoArgsConstructor
@DynamoDbBean
class Item {

    String username

    String itemId

    String creationTimestamp

    String lastUpdatedTimestamp

    @DynamoDbPartitionKey
    String getUsername() {
        username
    }

    @DynamoDbSortKey
    @DynamoDbSecondarySortKey(indexNames = [ "items-global-chronological-index"])
    String getItemId() {
        itemId
    }

    @DynamoDbSecondaryPartitionKey(indexNames = [ "items-global-chronological-index" ])
    String getSecondaryPartitionKey() {
        "1"
    }

    String getCreationTimestamp() {
        creationTimestamp
    }

    String getLastUpdatedTimestamp() {
        lastUpdatedTimestamp
    }
}

Run the code above to create the table in a Spring Boot Test. Use the following configuration class:

@Configuration
class TestDynamoDbConfig {

    @Value('${app.config.dynamodb.items.table}')
    private String dynamoDbTableName

    @Bean
    @Primary
    DynamoDbTable<Item> testDynamoDbTable() {
        DynamoDbEnhancedClient.builder().dynamoDbClient(DynamoDbClient.builder().credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("key", "secret"))).endpointOverride(new URI("http://localhost:8000")).build())
                .build().table(dynamoDbTableName, TableSchema.fromBean(Item.class))
    }
}

And run the following by calling from the main method or something:

@Component
class DefaultItemService  {
    
    private DynamoDbTable<Item> table

    private final String gsiName

    DefaultItemService(DynamoDbTable<Item> dynamoDbTable, @Value('${app.config.dynamodb.item.secondaryIndex.name}') String secondaryIndex) {
        this.table = dynamoDbTable
        this.gsiName = secondaryIndex
    }

    void query() {
        DynamoDbIndex<Item> index = table.index(gsiName)

        QueryConditional queryConditional = QueryConditional.keyEqualTo(Key.builder().partitionValue("1").build())

        QueryEnhancedRequest queryEnhancedRequest = QueryEnhancedRequest.builder().queryConditional(queryConditional).build()

        Item item = index.query(queryEnhancedRequest)
                            .first()
                            .items()
                            .first()
    }
}

The end result of this should be an error like this:

software.amazon.awssdk.services.dynamodb.model.DynamoDbException: Query condition missed key schema element

Possible Solution

No response

Additional Information/Context

No response

AWS Java SDK version used

2.32.31

JDK version used

openjdk version "17.0.16" 2025-07-15. Groovy 3.0.20

Operating System and version

Linux Mint 21.3 x86_64

Metadata

Metadata

Assignees

Labels

bugThis issue is a bug.dynamodb-enhancedresponse-requestedWaiting on additional info and feedback. Will move to "closing-soon" in 10 days.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions