Problem
The SQS emulator only accepts the legacy XML query protocol (form-encoded Action=…), but @aws-sdk/client-sqs defaults to the AWS JSON 1.0 protocol on every recent release. Pointing a current AWS SDK v3 SQS client at the emulator fails on the first call.
Reproduction
Against emulate@0.5.0, using @aws-sdk/client-sqs@3.1042.0:
import { SQSClient, ListQueuesCommand } from "@aws-sdk/client-sqs";
const sqs = new SQSClient({
endpoint: "http://localhost:4007/sqs",
region: "us-east-1",
credentials: {
accessKeyId: "AKIAIOSFODNN7EXAMPLE",
secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
},
});
await sqs.send(new ListQueuesCommand({}));
SyntaxError: Unexpected token '<', "<?xml vers"... is not valid JSON
The same XML body is shown verbatim in client-iam and client-sts tests via the documented endpoint: "…/sts" / …/iam" workaround, but those services do work because their SDK clients still speak the query protocol. SQS does not.
Wire-level trace
Captured via a custom NodeHttpHandler to see exactly what crosses the socket.
What the SDK sends:
|
|
| Method |
POST |
| URL |
http://localhost:4007/sqs/ |
Content-Type |
application/x-amz-json-1.0 |
X-Amz-Target |
AmazonSQS.ListQueues |
| Body |
{} |
What the emulator returns:
|
|
| Status |
400 |
Content-Type |
application/xml |
| Body |
see below |
<?xml version="1.0" encoding="UTF-8"?>
<ErrorResponse>
<Error>
<Code>InvalidAction</Code>
<Message>The action is not valid for this endpoint.</Message>
</Error>
<RequestId>…</RequestId>
</ErrorResponse>
The emulator parses the request as XML query, finds no Action= form parameter, and returns InvalidAction in XML. The SDK then runs the XML body through its JSON deserializer and surfaces the misleading Unexpected token '<' SyntaxError, which gives no hint that protocol negotiation is the underlying problem.
Root cause
AWS migrated SQS to AwsJson1.0 in 2023; @aws-sdk/client-sqs switched its default protocol to JSON shortly after, and every recent SDK lockfile pulls the JSON-by-default version. The emulator's packages/@emulators/aws/src/routes/sqs.ts only registers handlers under POST /sqs/ that read the form-encoded Action parameter, so any JSON-RPC request lands on the same route, fails the action-name check, and gets the XML error.
AWS announcement (CBOR / AwsJson1.0 for SQS): https://aws.amazon.com/blogs/developer/announcing-cbor-support-and-other-improvements-for-amazon-sqs/
Suggested fixes
Listed roughly in increasing scope:
- Dispatch on protocol headers in the SQS route. When
Content-Type: application/x-amz-json-1.0 and X-Amz-Target: AmazonSQS.<Action> are present, parse the JSON body and respond with application/x-amz-json-1.0. Keep the existing XML query handler as a fallback (X-Amz-Query-Mode: true or no JSON headers). Smallest behavioral surface, most compatible with current SDK versions.
- Implement the JSON protocol path and treat XML query as the fallback. Mirrors the real AWS migration and avoids the SDK-version-pinning workaround that the README would otherwise need to document.
- At minimum, document the limitation. README currently doesn't mention that the SQS emulator requires the XML query protocol. A note pointing users to either pin
@aws-sdk/client-sqs to a pre-JSON release or hit the endpoint via fetch/curl would at least save people from the cryptic Unexpected token '<' chase.
Happy to send a PR for (1) — that path is contained inside packages/@emulators/aws/src/routes/sqs.ts and a small request-shape adapter — once the maintainers confirm the direction.
Environment
- emulate: 0.5.0
- @aws-sdk/client-sqs: 3.1042.0
- @smithy/node-http-handler: 4.6.1 (used to capture wire-level traffic)
- Node.js: 24
- OS: macOS 15
Problem
The SQS emulator only accepts the legacy XML query protocol (form-encoded
Action=…), but@aws-sdk/client-sqsdefaults to the AWS JSON 1.0 protocol on every recent release. Pointing a current AWS SDK v3 SQS client at the emulator fails on the first call.Reproduction
Against
emulate@0.5.0, using@aws-sdk/client-sqs@3.1042.0:The same XML body is shown verbatim in
client-iamandclient-ststests via the documentedendpoint: "…/sts"/…/iam"workaround, but those services do work because their SDK clients still speak the query protocol. SQS does not.Wire-level trace
Captured via a custom
NodeHttpHandlerto see exactly what crosses the socket.What the SDK sends:
POSThttp://localhost:4007/sqs/Content-Typeapplication/x-amz-json-1.0X-Amz-TargetAmazonSQS.ListQueues{}What the emulator returns:
400Content-Typeapplication/xmlThe emulator parses the request as XML query, finds no
Action=form parameter, and returnsInvalidActionin XML. The SDK then runs the XML body through its JSON deserializer and surfaces the misleadingUnexpected token '<'SyntaxError, which gives no hint that protocol negotiation is the underlying problem.Root cause
AWS migrated SQS to AwsJson1.0 in 2023;
@aws-sdk/client-sqsswitched its default protocol to JSON shortly after, and every recent SDK lockfile pulls the JSON-by-default version. The emulator'spackages/@emulators/aws/src/routes/sqs.tsonly registers handlers underPOST /sqs/that read the form-encodedActionparameter, so any JSON-RPC request lands on the same route, fails the action-name check, and gets the XML error.Suggested fixes
Listed roughly in increasing scope:
Content-Type: application/x-amz-json-1.0andX-Amz-Target: AmazonSQS.<Action>are present, parse the JSON body and respond withapplication/x-amz-json-1.0. Keep the existing XML query handler as a fallback (X-Amz-Query-Mode: trueor no JSON headers). Smallest behavioral surface, most compatible with current SDK versions.@aws-sdk/client-sqsto a pre-JSON release or hit the endpoint viafetch/curlwould at least save people from the crypticUnexpected token '<'chase.Happy to send a PR for (1) — that path is contained inside
packages/@emulators/aws/src/routes/sqs.tsand a small request-shape adapter — once the maintainers confirm the direction.Environment