-
Notifications
You must be signed in to change notification settings - Fork 625
Description
Checkboxes for prior research
- I've gone through Developer Guide and API referenceI've checked AWS Forums and StackOverflow.I've searched for previous similar issues and didn't find any solution.To pick up a draggable item, press the space bar. While dragging, use the arrow keys to move the item. Press space again to drop the item in its new position, or press escape to cancel.
Describe the bug
S3 API calls using virtual-host-style requests (default in most SDKs except for botocore) fail when using a single endpoint URL (e.g., AWS_ENDPOINT_URL=http://localhost.localstack.cloud:4566
or AWS_ENDPOINT_URL=http://localhost:4566
). This breaks native LocalStack compatibility of many AWS tools such as the AWS Toolkit (aws/aws-toolkit-vscode#2007) and AWS CDK (related to aws/aws-cdk#21014).
AWS endpoint standard: https://docs.aws.amazon.com/sdkref/latest/guide/feature-ss-endpoints.html
Regression Issue
- Select this option if this issue appears to be a regression.To pick up a draggable item, press the space bar. While dragging, use the arrow keys to move the item. Press space again to drop the item in its new position, or press escape to cancel.
SDK version number
@aws-sdk/client-s3@3.830.0
Which JavaScript Runtime is this issue in?
Node.js
Details of the browser/Node.js/ReactNative version
v22.14.0
Reproduction Steps
-
Install and start LocalStack: https://docs.localstack.cloud/getting-started/installation/
-
npm install @aws-sdk/client-s3@3.830.0
-
Run
node test.js
using the following script:import { S3Client, CreateBucketCommand, PutObjectCommand} from "@aws-sdk/client-s3"; const BUCKET_NAME = "my-unique-localstack-bucket-name"; const OBJECT_KEY = "HelloWorld.txt"; const CONTENT = "HelloWorld!"; const s3 = new S3Client({ region: "us-east-1", endpoint: "http://localhost:4566", credentials: { accessKeyId: "test", secretAccessKey: "test", }, maxAttempts: 1, }); const createBucketResponse = await s3.send(new CreateBucketCommand({ Bucket: BUCKET_NAME })); const putObjectResponse = await s3.send(new PutObjectCommand({ Bucket: BUCKET_NAME, Key: OBJECT_KEY, Body: CONTENT, ContentType: "text/plain", }));
Link reproducer test suite (including botocore comparison): https://github.com/joe4dev/s3-endpoint-url-testing
Observed Behavior
The JavaScript SDK raises an S3ServiceException [InternalError]: exception while calling s3 with unknown operation: Unable to find operation for request to service s3: PUT
The root cause of this confusing ProtocolParserError is that emulators such as LocalStack cannot reliably detect virtual host-styled S3 requests without the .s3
prefix when using a single global endpoint. Therefore, the PutBucket (or any virtual-host-style request) fails with an internal server error (i.e., it gets interpreted as an XML-style CreateBucket request).
/Users/joe/Downloads/s3_test/node_modules/@smithy/smithy-client/dist-cjs/index.js:388
const response = new exceptionCtor({
^
S3ServiceException [InternalError]: exception while calling s3 with unknown operation: Unable to find operation for request to service s3: PUT /
at throwDefaultError (/Users/joe/Downloads/s3_test/node_modules/@smithy/smithy-client/dist-cjs/index.js:388:20)
at /Users/joe/Downloads/s3_test/node_modules/@smithy/smithy-client/dist-cjs/index.js:397:5
at de_CommandError (/Users/joe/Downloads/s3_test/node_modules/@aws-sdk/client-s3/dist-cjs/index.js:4953:14)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
at async /Users/joe/Downloads/s3_test/node_modules/@smithy/middleware-serde/dist-cjs/index.js:36:20
at async /Users/joe/Downloads/s3_test/node_modules/@aws-sdk/middleware-sdk-s3/dist-cjs/index.js:484:18
at async /Users/joe/Downloads/s3_test/node_modules/@smithy/middleware-retry/dist-cjs/index.js:320:38
at async /Users/joe/Downloads/s3_test/node_modules/@aws-sdk/middleware-sdk-s3/dist-cjs/index.js:110:22
at async /Users/joe/Downloads/s3_test/node_modules/@aws-sdk/middleware-sdk-s3/dist-cjs/index.js:137:14
at async /Users/joe/Downloads/s3_test/node_modules/@aws-sdk/middleware-logger/dist-cjs/index.js:33:22 {
'$fault': 'client',
'$metadata': {
httpStatusCode: 500,
requestId: 'a142a92d-7db0-497f-bf8f-11011f69899f',
extendedRequestId: 's9lzHYrFp76ZVxRcpX9+5cjAnEH2ROuNkd2BHfIa6UkFVdtjf5mKR3/eTPFvsiP/XV/VLi31234=',
cfId: undefined,
attempts: 1,
totalRetryDelay: 0
},
Code: 'InternalError',
RequestId: 'a142a92d-7db0-497f-bf8f-11011f69899f'
}
Node.js v22.14.0
Expected Behavior
S3 requests should work properly when using a single endpoint (e.g., AWS_ENDPOINT_URL=http://localhost:4566
), following the AWS standard for endpoint URLs: https://docs.aws.amazon.com/sdkref/latest/guide/feature-ss-endpoints.html
The S3 client in the JavaScript SDK should detect that a single endpoint is incompatible with virtual-host-style requests and switch to path-style.
Testing virtual-host-style should still be possible with emulators when using a compatible service-specific endpoint URL such as AWS_ENDPOINT_URL_S3=http://s3.localhost.localstack.cloud:4566
Possible Solution
Link PR: #7137
The Python library botocore could serve as a reference implementation, as demonstrated in the PR#2785 Endpoint resolution v2.0.
The relevant change happens in compute_endpoint_resolver_builtin_defaults where this conditional adjusts force_path_style
if a custom client_endpoint_url
is used and the endpoint does not match the S3 Accelerate endpoint scheme (only applicable to amazonaws.com
endpoints). It forces path
-style S3 requests unless the S3 addressing_style
configuration explicitly demands virtual
-style:
elif client_endpoint_url is not None and not is_s3_accelerate_url(
client_endpoint_url
):
force_path_style = s3_config.get('addressing_style') != 'virtual'
This change enables S3 APIs to work with a global endpoint (e.g., AWS_ENDPOINT_URL=http://localhost.localstack.cloud
) using path
-style S3 requests while still allowing to force virtual
-style S3 requests if demanded (e.g., when manually setting AWS_ENDPOINT_URL_S3=http://s3.localhost.localstack.cloud:4566
and s3 > addressing_style=virtual
).
The solution from botocore is not directly applicable here because the JavaScript SDK offers a boolean-named configuration forcePathStyle=true|false
(auto by default) instead of the triple-choice option addressing_style=auto|virtual|path
. We should allow for virtual-style-requests, either by interpreting forcePathStyle=false
(if auto reliably detect undefined) as addressing_style_=virtual
or by detecting compatible subdomains starting with s3.
(e.g., http://s3.localhost.localstack.cloud:4566
).
Additional Information/Context
This issue may be related to Smithy code generation and might be solvable using the Smithy rules engine
Related example from botocore for S3 endpoint ruleset: https://github.com/boto/botocore/blob/d1fd992119b5df4f4d2169e2383ab99288466e8b/botocore/data/s3/2006-03-01/endpoint-rule-set-1.json
Disclaimer: I work for LocalStack
Activity
kuhe commentedon Jun 25, 2025
Do you currently instruct LocalStack users to set
forcePathStyle
on theS3Client
instance constructor under applicable conditions?joe4dev commentedon Jun 26, 2025
Yes, we do for the AWS JavaScript SDK integration: https://docs.localstack.cloud/aws/integrations/aws-sdks/javascript/
AWS_ENDPOINT_URL_S3=http://s3.localhost.localstack.cloud:4566
forcePathStyle: true
as a fallback (e.g., if DNS rebind protection blocks resolvinglocalhost.localstack.cloud
to127.0.0.1
)Nevertheless, customers expect this to work out of the box using a single
AWS_ENDPOINT_URL
and for indirect usage through tools such as the AWS Toolkit for VSCode, this is not configurable.Edit (2025-07-15): Updated SDK documentation link pointing to our new documentation.
aBurmeseDev commentedon Jul 16, 2025
Checking in here. This's the similar behavior that you requested in another issue and we responded.
TLDR; This behavior is specified at the model level (S3's model file here) and the SDKs accept this as the default "out-of-the-box" behavior as dictated by the service. The change in behavior will potentially break current users.
github-actions commentedon Jul 16, 2025
This issue is now closed. Comments on closed issues are hard for our team to see.
If you need more assistance, please open a new issue that references this one.
github-actions commentedon Jul 31, 2025
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs and link to relevant comments in this thread.