Description
Describe the bug
When calling Ec2Client#describeInstances
with one or more filter expressions, the filters contain numbers formatted using the current locale rather than the root locale. Thus for instance running in the ne
(Nepalese) local the query uses Filter.%E0%A5%A7.Name
rather than Filter.1.Name
, where %E0%A5%A7
is the UTF-8 encoding for U+0967 DEVANAGARI DIGIT ONE
which is the rendering for the integer 1
in this locale. The full HTTP request in this case is as follows:
POST / HTTP/1.1
Host: 127.0.0.1:60608
amz-sdk-invocation-id: a5d80d84-b502-096b-bef6-da85818cb947
amz-sdk-request: attempt=1; max=10
Authorization: AWS4-HMAC-SHA256 Credential=ec2_key/20250318/es-test-region/ec2/aws4_request, SignedHeaders=amz-sdk-invocation-id;amz-sdk-request;content-length;content-type;host;x-amz-content-sha256;x-amz-date, Signature=749ddaeccbe94b749d6c1e7446a070dcca7ec2845068053883f28fdd9129ded3
Content-Type: application/x-www-form-urlencoded; charset=utf-8
User-Agent: aws-sdk-java/2.30.38 md/io#sync md/http#Apache ua/2.1 os/Mac_OS_X#15.3.2 lang/java#23 md/OpenJDK_64-Bit_Server_VM#23+37 md/vendor#Eclipse_Adoptium md/en_GB cfg/auth-source#stat m/D
x-amz-content-sha256: 1380541b646459c65b2316cb6c26f03a41760ebe57c48ecbe55697c5da1b3938
X-Amz-Date: 20250318T142438Z
Content-Length: 239
Connection: Keep-Alive
Action=DescribeInstances&Version=2016-11-15&Filter.%E0%A5%A7.Name=instance-state-name&Filter.%E0%A5%A7.Value.%E0%A5%A7=running&Filter.%E0%A5%A7.Value.%E0%A5%A8=pending&Filter.%E0%A5%A8.Name=tag%3Astage&Filter.%E0%A5%A8.Value.%E0%A5%A7=prod
EC2 rejects this with a 400 Bad request
:
[2025-03-19T09:36:24,478][DEBUG][o.e.d.e.AwsEc2SeedHostsProvider] [node-0] Full exception:software.amazon.awssdk.services.ec2.model.Ec2Exception: Value (१) for parameter QueryStringParameter is invalid. Invalid or illegal XML character specified (Service: Ec2, Status Code: 400, Request ID: 7405f831-6f63-4bd1-b54f-f96be228b4c8) (SDK Attempt Count: 1)
The problem is that software.amazon.awssdk.protocols.query.internal.marshall.ListQueryMarshaller#EC2_QUERY_PATH_RESOLVER
uses a bare String.format("%s.%d", path, i + 1)
rather than String.format(Locale.ROOT, "%s.%d", path, i + 1)
Regression Issue
- Select this option if this issue appears to be a regression.
Expected Behavior
In all locales, we should render the filters using ASCII digits 0
to 9
only:
Action=DescribeInstances&Version=2016-11-15&Filter.1.Name=instance-state-name&Filter.1.Value.1=running&Filter.1.Value.2=pending&Filter.2.Name=tag%3Astage&Filter.2.Value.1=prod
Current Behavior
We render the filters using locale-dependent numerals, UTF-8-encoded, for instance in the ne
locale:
Action=DescribeInstances&Version=2016-11-15&Filter.%E0%A5%A7.Name=instance-state-name&Filter.%E0%A5%A7.Value.%E0%A5%A7=running&Filter.%E0%A5%A7.Value.%E0%A5%A8=pending&Filter.%E0%A5%A8.Name=tag%3Astage&Filter.%E0%A5%A8.Value.%E0%A5%A7=prod
Reproduction Steps
Attempt to invoke Ec2Client#describeInstances
in a locale that does not render numerals using the ASCII digits 0
to 9
, such as for instance ne
.
Possible Solution
diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/ListQueryMarshaller.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/ListQueryMarshaller.java
index 971dbb96f95..4323fd80b1c 100644
--- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/ListQueryMarshaller.java
+++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/ListQueryMarshaller.java
@@ -31,7 +31,7 @@ public class ListQueryMarshaller implements QueryMarshaller<List<?>> {
listTrait.isFlattened() ?
String.format("%s.%d", path, i + 1) :
String.format("%s.%s.%d", path, listTrait.memberFieldInfo().locationName(), i + 1);
- private static final PathResolver EC2_QUERY_PATH_RESOLVER = (path, i, listTrait) -> String.format("%s.%d", path, i + 1);
+ private static final PathResolver EC2_QUERY_PATH_RESOLVER = (path, i, listTrait) -> String.format(Locale.ROOT, "%s.%d", path, i + 1);
private static final EmptyListMarshaller AWS_QUERY_EMPTY_LIST_MARSHALLER =
(context, path) -> context.request().putRawQueryParameter(path, "");
Additional Information/Context
No response
AWS Java SDK version used
2.30.38
JDK version used
OpenJDK Runtime Environment Temurin-23+37 (build 23+37)
Operating System and version
Mac OS X 15.3.2 aarch64/Oracle Corporation 23 (64-bit)/cpus=12,threads=1,free=493903224,total=536870912