Skip to content

SQS Deserialization Error: Cannot read properties of undefined (reading 'Type') #7749

@sourabhketkale

Description

@sourabhketkale

Checkboxes for prior research

Describe the bug

The AWS SDK for JavaScript v3 throws a TypeError: Cannot read properties of undefined (reading 'Type') when deserializing SQS error responses that return HTTP 500 with Content-Type: text/xml but an empty or malformed body.

This error occurs during the SDK's internal deserialization process before the error can be caught and processed by application code.

Note: This bug was also present in v3.917.0. Also, see related issue #2861.

Regression Issue

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

SDK version number

@aws-sdk/client-sqs@3.917.0 ... 3.89.0

Which JavaScript Runtime is this issue in?

Node.js

Details of the browser/Node.js/ReactNative version

v23.9.0

Reproduction Steps

I've a minimal reproduction script that creates a mock SQS server returning the problematic response. The script reproduces the error consistently on v3.989.0. We are using @aws-sdk/client-sqs:"^3.917.0"

Steps to Reproduce:

  1. Install @aws-sdk/client-sqs@3.989.0
  2. Copy the script below with any name eg: reproduce-sqs-error.mjs and Run script with command node reproduce-sqs-error.mjs
  3. Observe the TypeError when the SDK attempts to deserialize the 500 error response

NOTE: The attached script reproduce-sqs-error.mjs is majorly AI generated and used only to demonstrate the occurrence of the error. The stack trace attached is not from productions logs. But we are facing same issue in production.

Error popping jobs with message: popJobs() - client.send(ReceiveMessageCommand) error with message: 
Cannot read properties of undefined (reading 'Type') Deserialization error: to see the raw response, 
inspect the hidden field {error}.$response on this object.

This error happens intermittently when the AWS SQS service returns malformed error responses.

Script

Script with output
/**
 * Reproduce the "Cannot read properties of undefined (reading 'Type')" error locally.
 *
 * This script:
 * 1. Starts a tiny HTTP server that mimics a broken SQS endpoint
 *    (returns HTTP 500 with no proper <Error> XML body)
 * 2. Creates an SQS client pointing to that mock server
 * 3. Calls ReceiveMessageCommand — which triggers the exact deserialization crash
 *
 * Usage:
 *   node tmp/reproduce-sqs-error.mjs
 */

import http from 'node:http';
import {
  SQSClient,
  ReceiveMessageCommand,
} from '@aws-sdk/client-sqs';

// ─── Step 1: Mock SQS server that returns bad responses ───────────────────────
const PORT = 19324;

const server = http.createServer((req, res) => {
  console.log(`\n[Mock SQS] Received request: ${req.method} ${req.url}`);

  // Simulate different bad response scenarios. Uncomment the one you want to test:

  // Scenario A: HTTP 500 with empty body (most likely cause in prod)
  res.writeHead(500, { 'Content-Type': 'text/xml' });
  res.end('');

  // Scenario B: HTTP 500 with non-XML body (e.g., a load balancer error page)
  // res.writeHead(500, { 'Content-Type': 'text/html' });
  // res.end('<html><body>Service Unavailable</body></html>');

  // Scenario C: HTTP 503 with partial XML (missing <Error> structure)
  // res.writeHead(503, { 'Content-Type': 'text/xml' });
  // res.end('<?xml version="1.0"?><ErrorResponse><RequestId>test-123</RequestId></ErrorResponse>');

  // Scenario D: HTTP 200 with malformed XML (would fail during normal deserialization)
  // res.writeHead(200, { 'Content-Type': 'text/xml' });
  // res.end('not xml at all');
});

server.listen(PORT, async () => {
  console.log(`[Mock SQS] Listening on http://localhost:${PORT}`);
  console.log('[Mock SQS] Sending ReceiveMessageCommand...\n');

  // ─── Step 2: SQS client pointing to mock server ──────────────────────────
  const client = new SQSClient({
    region: 'us-east-1',
    endpoint: `http://localhost:${PORT}`,
    credentials: {
      accessKeyId: 'fake',
      secretAccessKey: 'fake',
    },
    // Disable retries so we see the error immediately
    maxAttempts: 1,
  });

  const params = {
    MessageSystemAttributeNames: ['ApproximateReceiveCount', 'SentTimestamp'],
    MaxNumberOfMessages: 10,
    MessageAttributeNames: ['jobName', 'xRequestId', 'xRequestTraceId'],
    QueueUrl: `http://localhost:${PORT}/queue/test-queue`,
    WaitTimeSeconds: 5,
  };

  // ─── Step 3: Trigger the error ────────────────────────────────────────────
  try {
    const command = new ReceiveMessageCommand(params);
    const data = await client.send(command);
    console.log('Unexpected success:', JSON.stringify(data, null, 2));
  } catch (err) {
    console.log('='.repeat(70));
    console.log('ERROR REPRODUCED!');
    console.log('='.repeat(70));

    // Actual error message
    console.log('\n Actual error message:', err.message);
    console.log('\nerr.name:', err.name);
    console.log('\nerr.$metadata:', JSON.stringify(err.$metadata, null, 2));

    // This is the hidden field the SDK tells you to inspect
    // Note: $response contains circular refs, so extract key fields
    if (err.$response) {
      console.log("hello in here")
      console.log('\nerr.$response.statusCode:', err.$response.statusCode);
      console.log('err.$response.headers:', JSON.stringify(err.$response.headers, null, 2));
      console.log('err.$response.body (type):', typeof err.$response.body);
    }
    console.log('\nerr.$responseBodyText:', err.$responseBodyText);

    // This is what the PR would log:
    console.log('\n--- What PR #1759 would log: ---');
    const safeResponse = err.$response ? { statusCode: err.$response.statusCode, headers: err.$response.headers } : undefined;
    console.log(`popJobs() - client.send(ReceiveMessageCommand) error with message: ${err.message}. Raw http Response: ${JSON.stringify(safeResponse)}. AWS Metadata: ${JSON.stringify(err.$metadata)}`);
  } finally {
    server.close();
    console.log('\n[Mock SQS] Server stopped.');
  }
});

/**
 * Output:
node reproduce-sqs-error.mjs 
[Mock SQS] Listening on http://localhost:19324
[Mock SQS] Sending ReceiveMessageCommand...


[Mock SQS] Received request: POST /
======================================================================
ERROR REPRODUCED!
======================================================================

 Actual error message: Cannot read properties of undefined (reading 'Type')
  Deserialization error: to see the raw response, inspect the hidden field {error}.$response on this object.

err.name: TypeError

err.$metadata: {
  "httpStatusCode": 500,
  "attempts": 1,
  "totalRetryDelay": 0
}
hello in here

err.$response.statusCode: 500
err.$response.headers: {
  "content-type": "text/xml",
  "date": "Fri, 13 Feb 2026 00:50:27 GMT",
  "connection": "keep-alive",
  "keep-alive": "timeout=5",
  "transfer-encoding": "chunked"
}
err.$response.body (type): object

err.$responseBodyText: undefined

popJobs() - client.send(ReceiveMessageCommand) error with message: Cannot read properties of undefined (reading 'Type')
  Deserialization error: to see the raw response, inspect the hidden field {error}.$response on this object.. Raw http Response: {"statusCode":500,"headers":{"content-type":"text/xml","date":"Fri, 13 Feb 2026 00:50:27 GMT","connection":"keep-alive","keep-alive":"timeout=5","transfer-encoding":"chunked"}}. AWS Metadata: {"httpStatusCode":500,"attempts":1,"totalRetryDelay":0}

[Mock SQS] Server stopped.

*/

Observed Behavior

The aws-sdk-v3/client-sqs throws:

TypeError: Cannot read properties of undefined (reading 'Type')
  Deserialization error: to see the raw response, inspect the hidden field {error}.$response on this object.

Commands where the error is seen for SQS:

  • ReceiveMessageCommand
  • DeleteMessageCommand

Expected Behavior

Should have no Deserialization Error: Cannot read properties of undefined (reading 'Type') error when a error response is received from SQS service.

Possible Solution

Add null-safe property access for both .Code AND .Type in the deserialization logic, similar to what was done in PR #4367.

Additional Information/Context

No response

Metadata

Metadata

Assignees

Labels

bugThis issue is a bug.needs-triageThis issue or PR still needs to be triaged.pending-releaseThis issue will be fixed by an approved PR that hasn't been released yet.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions