All notable changes to com.babelqueue:babelqueue-sqs are documented here.
The format is based on Keep a Changelog, and
this project adheres to Semantic Versioning.
The envelope wire format is versioned separately by meta.schema_version
(currently 1) — see the contract at babelqueue.com.
SqsConsumernow applies the §3.7 schema-version gate before decoding the body: when thebq-schema-versionmessage attribute is present and is not the supported version (1), the body is never decoded and the handler is never called; the message is reported viaonError(with anullenvelope) and is not deleted, so SQS redrives it to the DLQ. A missing or blank attribute (empty or ASCII-whitespace-only) keeps the existing decode path. Cross-SDK parity (GR-5): the value is compared exactly, with no trimming.onErrormay now receive anullenvelope for a schema-version gate rejection (the body is not decoded, so there is no envelope); callbacks that dereference the envelope should null-check it.
MINOR release: the failure path's behaviour and the public Builder API change.
- Release now follows broker-bindings.md §3 — behaviour change. A throwing handler's
message is always released with
ChangeMessageVisibility(ReceiptHandle, VisibilityTimeout = backoff)instead of being left to wait out the queue's full visibility timeout; it is still never deleted. The backoff is the newBuilder.releaseDelaySeconds(int), default0= redeliver now. Previously a failing message reappeared only after the visibility timeout (e.g. 30 s). SetreleaseDelaySeconds(N)to keep a delay between attempts. - Poison-loop risk. With the default
0, a message that fails permanently is redelivered on every receive, and a short outage can burn throughmaxReceiveCountin under a second. Configure aRedrivePolicyon the queue (deadLetterTargetArn+maxReceiveCount) so SQS moves such a message to a native DLQ, and pick a non-zeroreleaseDelaySecondsif the handler depends on something that can be briefly unavailable. - Out-of-range delays are clamped to 0–43200 s (the SQS 12 h cap), like the Go and Python transports; they no longer throw.
- Require
com.babelqueue:babelqueue-core 1.8.0(unknown-key preservation,Envelope.withAttempts).
- A failed
DeleteMessageafter a successful handler (throttling, network, stale receipt handle) is no longer treated as a handler failure: it is reported toonErroras the newSqsDeleteException(broker error as its cause) and the message is not released, so it is redelivered only after its visibility timeout instead of immediately. The same applies to thedelete/dead_letterunknown-URN strategies; a failed delete never escapespoll(). - A failed release (
ChangeMessageVisibilityrejected: stale receipt handle, throttling, network) no longer escapespoll()/run(). The handler error is reported toonErrorfirst, then the release error; the batch continues and SQS still redelivers the message once its visibility timeout expires. attemptsreconciliation now preserves unknown top-level /metakeys (core 1.8.0 extras), so a newer producer's additions reach the handler unchanged.
- Unknown-URN strategy (
Builder.unknownUrnStrategy(String),fail|delete|release|dead_letter, the coreUnknownUrnStrategynames).releasecallsChangeMessageVisibilitywithBuilder.unknownUrnReleaseDelaySeconds(int)(default0, matching the Python and Node transports; clamped to 0–43200);dead_letterdegrades todelete(this transport has no DLQ publisher — the contract's "DLQ disabled" rule). Default stays backward compatible:fail, ordeletewhen onlyonUnknownUrnis set.onUnknownUrnis now invoked before every strategy. SqsConsumer.MAX_VISIBILITY_TIMEOUT_SECONDS(43200)..github/dependabot.yml(Maven + GitHub Actions, weekly).
- OpenTelemetry
traceparenttransport wiring (ADR-0028, v0.2).SqsPublishergainspublishWithHeaders(Envelope, Map<String,String>)— the produce-side seam the optional corecom.babelqueue.otel.HeaderSenderwires to: out-of-band headers (e.g. a W3Ctraceparent) ride on the SQSMessageAttributeschannel beside the contractbq-*attributes (SqsAttributes.projectWithHeaders: the contract projection wins a key collision, merge in sorted order, bounded by the SQS 10-attribute cap), never inside the frozen envelope (GR-1). NewSqsHeaders.of(Message)surfaces a delivered message's attributes as aMap<String,String>, the consume-side seam forTracing.wrapHandler(tracer, handler, Supplier), so a carriedtraceparentmakes the consumer span a true child of the producer span. A header-less publish is byte-identical to before;trace_idis preserved (GR-4);schema_versionstays 1. No new runtime dependency — the header seam is a plainMap<String,String>; OpenTelemetry is needed only by callers who opt in.
- Require
com.babelqueue:babelqueue-core 1.5.0(the out-of-band header-carrier seam).
1.0.0 - 2026-06-12
- Initial release. An Amazon SQS transport on
babelqueue-core+ AWS SDK for Java v2:SqsPublisher(canonical-envelopeSendMessagewith the §3MessageAttributesprojection —bq-job/bq-trace-id/bq-message-id/bq-schema-version/bq-source-lang/bq-created-at; FIFO group/dedup) andSqsConsumer(long-poll receive → URN-routedBabelHandlers →DeleteMessage; SQS-native visibility-timeout retry;attemptsreconciled toApproximateReceiveCount − 1, never lowering a runtime-incremented count;onError/onUnknownUrnhooks). Java 17, JUnit 5, JaCoCo ≥90% line coverage (currently ~94%); unit tests run against a fakeSqsClient(no AWS, no network). The envelope is unchanged (schema_version: 1); SQS is purely additive.