@aws-sdk/client-sqs
>=3.0.0 <4.0.0postconditions14functions9last verified2026-06-24coverage score100%Postconditions: what we check
- send · aws-service-errorerrorWhenAny AWS service error or network failure: queue does not exist (QueueDoesNotExist), insufficient permissions (AccessDeniedException), request throttling (RequestThrottled / ThrottlingException), invalid message content (InvalidMessageContents), receipt handle expired (ReceiptHandleIsInvalid), KMS key errors, service unavailable, or any network-level failure (DNS, timeout, connection refused)Throws
SQSServiceException subclass with error.name set to the specific error code (e.g., "QueueDoesNotExist", "RequestThrottled", "AccessDeniedException", "ReceiptHandleIsInvalid"). For network errors, throws a generic Error or SdkClientError with a connection/timeout message.Required handlingCaller MUST wrap client.send() in try-catch. All SQS operations can fail due to network issues, permission problems, queue deletion, or AWS service outages. Unhandled rejections cause silent message loss (for SendMessageCommand) or crash consumer loops (for ReceiveMessageCommand). Minimum handling: try { await sqsClient.send(new SendMessageCommand({ QueueUrl, MessageBody })); } catch (error) { if (error instanceof SQSServiceException) { console.error(`SQS error [${error.name}]: ${error.message}`); } else { console.error('Network error:', error); } throw error; } For consumer loops using ReceiveMessageCommand, error handling is especially critical — an unhandled rejection crashes the polling loop and stops all message processing: try { const response = await sqsClient.send(new ReceiveMessageCommand({ QueueUrl })); const messages = response.Messages ?? []; // process messages... } catch (error) { logger.error('Failed to receive SQS messages', { queueUrl: QueueUrl, error }); // Continue polling — do not rethrow unless fatal }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - send (SendMessageBatchCommand) · sqs-send-batch-failed-not-checkederrorWhenSendMessageBatchCommand result is used without checking result.Failed array. Individual message failures return HTTP 200 and go to result.Failed[], not thrown exceptions. Missing this check silently drops messages.Throws
Does NOT throw for individual message failures. Returns HTTP 200 with result.Failed as BatchResultErrorEntry[] containing Code, Id, Message, SenderFault fields. Only throws SQSServiceException for request-level errors (QueueDoesNotExist, TooManyEntriesInBatchRequest, BatchEntryIdsNotDistinct, BatchRequestTooLong, EmptyBatchRequest, RequestThrottled).Required handlingMUST check result.Failed after every SendMessageBatch call: const result = await client.send(new SendMessageBatchCommand({ ... })); if (result.Failed && result.Failed.length > 0) { for (const failure of result.Failed) { logger.error(`Message ${failure.Id} failed: ${failure.Code} - ${failure.Message}`); } // Retry or dead-letter the failed messages } Failure to check result.Failed causes silent message loss with no observable error.costhighin prodsilent failureusers seelost datavisibilitysilent - send (SendMessageBatchCommand) · sqs-send-batch-no-try-catcherrorWhenSendMessageBatchCommand is awaited without try-catch. Request-level errors (queue deleted, batch too large, throttled) throw SQSServiceException.Throws
SQSServiceException subclass: QueueDoesNotExist (queue URL wrong/deleted), TooManyEntriesInBatchRequest (>10 entries), BatchEntryIdsNotDistinct (duplicate IDs), BatchRequestTooLong (total payload exceeds 256KB), EmptyBatchRequest (no entries), RequestThrottled (rate limit exceeded), InvalidAddress, InvalidSecurity.Required handlingMUST wrap in try-catch for request-level failures AND check result.Failed for per-message failures: try { const result = await client.send(new SendMessageBatchCommand({ QueueUrl, Entries })); if (result.Failed?.length) { // handle per-message failures } } catch (error) { if (error instanceof SQSServiceException) { logger.error(`SQS batch error [${error.name}]: ${error.message}`); } throw error; }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[5] - send (SendMessageBatchCommand) · sqs-send-batch-invalid-entry-iderrorWhenSendMessageBatchCommand is called with one or more entries whose Id field contains characters outside the allowed set [A-Za-z0-9_-] or exceeds 80 characters. Common when callers leak database UUIDs that include dots / slashes, or pass upstream message IDs containing ':' or '/'. The ENTIRE batch is rejected — none of the messages are sent, including the well-formed ones.Throws
InvalidBatchEntryId (a SQSServiceException subclass with error.name === "InvalidBatchEntryId", $fault === "client"). The exception is thrown synchronously from the SDK before any messages are sent. No partial success — Successful and Failed arrays are not populated because the request never reached the service in a processable form.Required handlingMUST wrap in try-catch AND validate entry.Id formatting at the call site before sending the batch: const sanitize = (id: string) => id.replace(/[^A-Za-z0-9_-]/g, '_').slice(0, 80); try { const result = await client.send(new SendMessageBatchCommand({ QueueUrl, Entries: items.map((item, i) => ({ Id: sanitize(item.id), MessageBody: JSON.stringify(item), })), })); } catch (error) { if (error instanceof SQSServiceException && error.name === 'InvalidBatchEntryId') { logger.error('Batch entry IDs malformed — entire batch rejected', { entries: items.map(i => i.id), }); // Sanitize and retry, or fall back to per-message SendMessage } throw error; }costhighin prodimmediate exceptionusers seelost datavisibilityvisible - send (DeleteMessageBatchCommand) · sqs-delete-batch-failed-not-checkederrorWhenDeleteMessageBatchCommand result is used without checking result.Failed array. Individual deletion failures return HTTP 200 with failures in result.Failed[]. Undeleted messages remain visible again after visibility timeout, causing duplicate processing.Throws
Does NOT throw for individual deletion failures. Returns HTTP 200 with result.Failed as BatchResultErrorEntry[] (Code, Id, Message, SenderFault). Only throws SQSServiceException for request-level failures (QueueDoesNotExist, BatchEntryIdsNotDistinct, TooManyEntriesInBatchRequest, etc.).Required handlingMUST check result.Failed to detect partial failures: const result = await client.send(new DeleteMessageBatchCommand({ QueueUrl, Entries })); if (result.Failed && result.Failed.length > 0) { logger.error('Some messages not deleted', { failed: result.Failed }); // These messages will reappear after visibility timeout — handle appropriately }costhighin prodsilent failureusers seelost datavisibilitysilent - send (ReceiveMessageCommand) · sqs-receive-no-try-catcherrorWhenReceiveMessageCommand is awaited in a polling loop without try-catch. Any thrown exception crashes the entire consumer loop, stopping all message processing silently until the process is restarted.Throws
SQSServiceException subclass: OverLimit (max in-flight messages reached — queue backlog), QueueDoesNotExist (queue deleted while consumer running), RequestThrottled, InvalidAddress, InvalidSecurity, KMS errors. Network failures throw generic Error (DNS failure, timeout, connection refused).Required handlingMUST wrap receive in try-catch — especially in polling loops. Never rethrow transient errors from the polling loop body: while (running) { try { const result = await client.send(new ReceiveMessageCommand({ QueueUrl, MaxNumberOfMessages: 10 })); const messages = result.Messages ?? []; for (const message of messages) { await processMessage(message); } } catch (error) { logger.error('SQS receive failed, retrying', { error }); await sleep(backoffMs); // Do NOT rethrow — rethrow crashes the loop } }costhighin proddelayed failureusers seedegraded performancevisibilitysilent - send (ReceiveMessageCommand) · sqs-receive-messages-undefinedwarningWhenresponse.Messages is accessed directly without null/undefined guard. When the queue is empty or all messages are in-flight, Messages may be undefined, causing TypeError: Cannot read properties of undefined (reading 'length').Throws
Does NOT throw from SQS — throws TypeError in caller code when accessing undefined.Messages.forEach() or .length. AWS SDK may return Messages as undefined (not empty array) when no messages are available.Required handlingMUST use null-coalescing when accessing Messages: const messages = result.Messages ?? []; // NOT: result.Messages.forEach(...) — crashes on empty queuecostlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[10] - send (DeleteMessageCommand) · sqs-delete-receipt-handle-invalidwarningWhenDeleteMessageCommand is called with a stale or expired receipt handle. Receipt handles expire after the message visibility timeout. Using a handle from a previous receive call may silently fail (request succeeds but message not deleted) or throw ReceiptHandleIsInvalid.Throws
ReceiptHandleIsInvalid (SQSServiceException subclass): thrown when the receipt handle is completely invalid. However, using a stale handle (from a prior receive when the message was received again) may return HTTP 200 while not deleting the intended message.Required handlingMUST always use the receipt handle from the most recent receive call for the message. Always wrap in try-catch: try { await client.send(new DeleteMessageCommand({ QueueUrl, ReceiptHandle: message.ReceiptHandle })); } catch (error) { if (error.name === 'ReceiptHandleIsInvalid') { // Message visibility timeout expired — message already back in queue logger.warn('Receipt handle expired', { messageId: message.MessageId }); } else { throw error; } }costmediumin prodsilent failureusers seelost datavisibilitysilentSources[11] - send (CreateQueueCommand) · sqs-create-queue-no-try-catcherrorWhenCreateQueueCommand is awaited without try-catch. Common in queue setup/init code that assumes the queue does not exist, but fails if called twice with different attributes or within 60s of deletion.Throws
QueueNameExists (400): queue exists with the same name but different attributes. QueueDeletedRecently (400): must wait 60 seconds after deleting before reusing name. InvalidAttributeName, InvalidAttributeValue: invalid queue configuration. RequestThrottled: rate limit exceeded.Required handlingMUST wrap in try-catch. CreateQueue is idempotent for same name+attributes — handle QueueNameExists by checking if the existing queue has the expected config: try { const result = await client.send(new CreateQueueCommand({ QueueName, Attributes })); return result.QueueUrl; } catch (error) { if (error.name === 'QueueDeletedRecently') { // Must wait 60s — implement retry with delay throw new Error('Queue was recently deleted, retry after 60 seconds'); } throw error; }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[12] - send (PurgeQueueCommand) · sqs-purge-in-progresswarningWhenPurgeQueueCommand is called a second time within 60 seconds of a prior purge on the same queue. AWS enforces a 60-second cooldown per queue. Common in test teardown scripts that call purge without checking.Throws
PurgeQueueInProgress (SQSServiceException): thrown when purge was already requested within the last 60 seconds. The previous purge may still be in progress deleting messages.Required handlingMUST wrap in try-catch and handle the cooldown: try { await client.send(new PurgeQueueCommand({ QueueUrl })); } catch (error) { if (error.name === 'PurgeQueueInProgress') { logger.warn('Queue purge already in progress, waiting 60s'); await sleep(60000); await client.send(new PurgeQueueCommand({ QueueUrl })); // retry } else { throw error; } }costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[13] - send (ChangeMessageVisibilityCommand) · sqs-change-visibility-not-inflightwarningWhenChangeMessageVisibilityCommand is called after the message's visibility timeout has already expired (message returned to queue). The receipt handle is still valid but the message is no longer in-flight, causing MessageNotInflight to be thrown.Throws
MessageNotInflight (SQSServiceException): the message is no longer being processed (timeout expired, returned to queue). ReceiptHandleIsInvalid if the receipt handle is completely invalid.Required handlingMUST wrap in try-catch. If MessageNotInflight is caught, the message has already been re-queued and is likely being processed by another consumer: try { await client.send(new ChangeMessageVisibilityCommand({ QueueUrl, ReceiptHandle: message.ReceiptHandle, VisibilityTimeout: 30 })); } catch (error) { if (error.name === 'MessageNotInflight') { // Message timed out — stop processing, it will be handled by another consumer logger.warn('Message visibility expired, abandoning', { messageId }); return; } throw error; }costmediumin prodsilent failureusers seelost datavisibilitysilentSources[14] - send (SendMessageCommand) · sqs-send-invalid-message-contentserrorWhenSendMessageCommand is called with a message body containing characters outside the allowed Unicode set. SQS only allows: #x9, #xA, #xD, #x20-#xD7FF, #xE000-#xFFFD, #x10000-#x10FFFF. Common when serializing arbitrary user input, binary data, or control characters (e.g., \x00-\x08, \x0B-\x0C, \x0E-\x1F).Throws
InvalidMessageContents (SQSServiceException, HTTP 400): the message is rejected entirely — not delivered to the queue. No partial delivery occurs.Required handlingMUST validate or sanitize message body before calling SendMessageCommand when the source data may contain arbitrary user input or binary content. Use Base64 encoding for binary payloads, or strip/replace invalid characters: // Option A: Base64 encode binary/arbitrary content const messageBody = Buffer.from(rawData).toString('base64'); // Option B: Validate characters before sending const VALID_SQS_CHARS = /^[\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD\u{10000}-\u{10FFFF}]*$/u; if (!VALID_SQS_CHARS.test(messageBody)) { throw new Error('Message body contains invalid SQS characters — encode as Base64'); } try { await client.send(new SendMessageCommand({ QueueUrl, MessageBody: messageBody })); } catch (error) { if (error.name === 'InvalidMessageContents') { throw new Error(`SQS rejected message: invalid characters in body`); } throw error; }costmediumin prodimmediate exceptionusers seelost datavisibilityvisible - send (SendMessageCommand) · sqs-send-kms-errorserrorWhenSendMessageCommand is called on a queue with server-side encryption (SSE) using AWS KMS, and the KMS key is unavailable, misconfigured, or the caller lacks permissions. KMS errors are categorically distinct from SQS service errors and require different remediation (IAM policy changes, KMS key rotation, key state fixes) rather than retry logic.Throws
One of 7 KmsXxx error classes (all SQSServiceException subclasses, HTTP 400): KmsAccessDenied (caller lacks kms:GenerateDataKey permission), KmsDisabled (KMS key is disabled), KmsInvalidKeyUsage (key not configured for encryption), KmsInvalidState (key in PENDING_DELETION or other invalid state), KmsNotFound (KMS key ARN does not exist or is in wrong region), KmsOptInRequired (KMS not enabled for the AWS account in this region), KmsThrottled (KMS request rate exceeded — usually transient).Required handlingMUST distinguish KMS errors from generic SQS errors — KMS errors require infrastructure fixes, not message-level retry: try { await client.send(new SendMessageCommand({ QueueUrl, MessageBody })); } catch (error) { if (error.name?.startsWith('Kms')) { // KMS infrastructure error — alert ops team, do not retry indefinitely logger.error('SQS KMS encryption failure', { errorCode: error.name, queueUrl: QueueUrl, // KmsThrottled is transient; others require IAM/key fixes retryable: error.name === 'KmsThrottled', }); throw error; } if (error instanceof SQSServiceException) { logger.error(`SQS error [${error.name}]: ${error.message}`); } throw error; } KmsThrottled is transient and can be retried with backoff. All other Kms* errors require infrastructure remediation (IAM policies, key state, key ARN).costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - send (SendMessageCommand) · sqs-send-fifo-missing-message-group-iderrorWhenSendMessageCommand is called on a FIFO queue (.fifo suffix in QueueUrl) without providing MessageGroupId. FIFO queues require MessageGroupId to organize messages into ordered processing groups. Omitting it causes the action to fail. Teams that initially build with standard queues and later migrate to FIFO queues commonly hit this error because the parameter is not required in the SDK type signature.Throws
InvalidParameterValue or UnsupportedOperation (SQSServiceException, HTTP 400): the action fails — the message is NOT sent to the queue. No partial delivery. Error occurs immediately, before any message is enqueued.Required handlingMUST provide MessageGroupId when sending to FIFO queues: const isFifoQueue = QueueUrl.endsWith('.fifo'); await client.send(new SendMessageCommand({ QueueUrl, MessageBody, // Required for FIFO queues: ...(isFifoQueue && { MessageGroupId: 'default' }), // Optional: override content-based deduplication: // MessageDeduplicationId: crypto.randomUUID(), })); FIFO queue requirements: 1. MessageGroupId: REQUIRED — messages in the same group are processed in FIFO order 2. MessageDeduplicationId: required only if ContentBasedDeduplication is NOT enabled on the queue. If enabled, SQS auto-generates a deduplication ID from the SHA-256 hash of the message body. Messages with identical MessageDeduplicationId within 5 minutes are silently deduplicated (only one copy delivered) — use UUIDs or content hashes to prevent accidental deduplication.costmediumin prodimmediate exceptionusers seelost datavisibilityvisible
Sources
Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.
- [1]docs.aws.amazon.com/AWSJavaScriptSDK/v3/latestSqs
- [2]aws.amazon.com/blogs/developer/service-error-handling-modular-aws-sdk-jsService Error Handling Modular Aws Sdk Js
- [3]docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReferenceAPI SendMessage
- [4]docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuideSqs Best Practices
- [5]docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReferenceAPI SendMessageBatch
- [6]docs.aws.amazon.com/AWSJavaScriptSDK/v3/latestSendMessageBatchCommand
- [7]docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReferenceAPI SendMessageBatchRequestEntry
- [8]docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReferenceAPI DeleteMessageBatch
- [9]docs.aws.amazon.com/AWSJavaScriptSDK/v3/latestDeleteMessageBatchCommand
- [10]docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReferenceAPI ReceiveMessage
- [11]docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReferenceAPI DeleteMessage
- [12]docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReferenceAPI CreateQueue
- [13]docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReferenceAPI PurgeQueue
- [14]docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReferenceAPI ChangeMessageVisibility
- [15]docs.aws.amazon.com/AWSJavaScriptSDK/v3/latestSendMessageCommand
- [16]docs.aws.amazon.com/kms/latest/developerguideServices Sqs
- [17]docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuideFIFO Queues Understanding Logic
Research notes
Curator notes from SOURCES.md captured when the profile was written so you can verify the reasoning, not just the rules.
Sources: @aws-sdk/client-sqs
Official Documentation
-
SDK v3 SQS Client Reference https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/sqs/ Complete API reference for SQSClient and all command classes.
-
SendMessage API Reference https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html Error codes: QueueDoesNotExist, InvalidMessageContents, MessageTooLong, RequestThrottled.
-
ReceiveMessage API Reference https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_ReceiveMessage.html Error codes: OverLimit, QueueDoesNotExist, RequestThrottled.
-
DeleteMessage API Reference https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_DeleteMessage.html Error codes: ReceiptHandleIsInvalid, QueueDoesNotExist, InvalidIdFormat.
-
AWS SDK v3 Error Handling Blog https://aws.amazon.com/blogs/developer/service-error-handling-modular-aws-sdk-js/ Official guidance on catching SQSServiceException subclasses.
-
SQS Best Practices https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-best-practices.html Performance and reliability best practices, error handling guidance.
-
SQS JavaScript SDK v3 Examples https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/javascript_sqs_code_examples.html Official AWS code examples.
Real-World Evidence
Backstage (GitHub: backstage/backstage, ~28k stars)
File: plugins/events-backend-module-aws-sqs/src/publisher/AwsSqsConsumingEventPublisher.ts
Pattern: Correct — all send() calls wrapped in try-catch with structured error logging.
Evidence quality: partial (correct usage, demonstrates the expected pattern)
Version Distribution (from test-repos/)
| Repo | Version declared |
|---|---|
| backstage | ^3.350.0 |
| trigger.dev | ^3.445.0 |