Azure SDK for C++ (September 2026)
The Azure SDK team is pleased to announce our September 2026 client library releases.
4 packages released this month.
Beta Packages (3)
-
AMQP
-
Azure Blob Storage Checkpoint Store
-
Event Hubs
Release highlights
AMQP 1.0.0-beta.13 Changelog
Features Added
- The Rust AMQP backend now generates SAS tokens from a shared access key. It sends CBS put-token requests with the
servicebus.windows.net:sastokentoken type. - Added support for the AMQP Decimal types (AmqpDecimal128, AmqpDecimal64, and AmqpDecimal32).
Bugs Fixed
-
MessageSendernow stores the negotiated maximum message size when its link attaches, andGetMaxMessageSizereturns that stored value instead of reading the link. The value is fixed when the peer’s ATTACH arrives and cannot change for the life of a link, but the read failed once the link was no longer attached, so callers were discovering a dead link from an exception thrown by a getter. The value is read again on each attach, so an entity whose maximum was reconfigured between links stays correct.MessageSender::IsLinkDetachedreports the same fact without throwing, and a close no longer waits for a DETACH from a peer that has already detached. The teardown still runs on every path. This change applies to the uAMQP transport. [#7389] - uAMQP pollable registration and removal no longer block each other while a poll is in flight. The polling registry now waits on completion notifications, and sender, receiver, and link setup and teardown do not hold connection locks across registry operations. The polling thread now sleeps when no pollable is registered. It spun on one core after a process closed its last connection. [#7370]
- uAMQP now tears down unsettled sends without leaving late dispositions with freed callback state. Sender Open cleanup no longer deadlocks with link polling. Sender Open, sender Close, and receiver Close report caller cancellation separately from synthetic timeout. [#7350]
- A close that fails now leaves the object closed.
ManagementClient,MessageSender, andMessageReceiverkept the open flag when the close threw, and the destructor then stopped the process. [#7323] - The connection no longer returns a cached CBS token that is at or near its expiry. It authenticates the audience again instead. [#7254]
- The connection now replaces each cached CBS token before that token expires, so a client that runs for longer than one token lifetime keeps working. This refresh applies to the uAMQP transport. Without this refresh, a send that gets the
amqp:unauthorized-accesscondition stops at the first attempt, because the Event Hubs producer treats that condition as not transient. [#7254] - The token refresh thread now wakes when a caller authenticates a new audience. Before, its wait tested the stop flag alone, so it slept on a deadline that it computed before that audience existed, and it left the new token unexamined for up to one minute. A token with a lifetime shorter than that delay could expire before its first refresh. This change applies to the uAMQP transport. [#7254]
- The management client now gets the current token for each operation and puts it in the
security_tokenapplication property. Before, it sent the token that it got when it opened. A management client lives as long as the client that owns it, so that token expired while the client continued to send it. The service reads that property: an operation that carries a token that is not valid gets theamqp:not-allowedcondition with status code 500, and Event Hubs does not count that condition as transient. This change applies to the uAMQP transport. [#7254] - An AMQP operation that the caller did not bound now gets a default deadline of 60 seconds, and a connection that goes to the error or the end state now wakes every operation that waits on it. A send on a connection that the service dropped waited forever on both transports: uAMQP stops the poll of the connection in those two states, and the Rust runtime blocks the calling thread with no bound. A receive keeps the deadline of the caller, because a receive is a long poll that the caller controls. The link attach and the link detach carry the same bound on both backends, because a rebuild runs both of them against the connection that died. The uAMQP sender attach also registers for the connection wake, so a connection that dies during a rebuild attach ends that wait. [#7254]
- The Rust connection now marks itself closed before it calls the native close. A close that reached its new bound returned an error and threw with the open flag still set. The Event Hubs recover path catches that exception and then destroys the connection, and the destructor stopped the process because the flag stayed set.
ManagementClient,MessageSender, andMessageReceiveralready follow this rule. [#7254] - Each uAMQP send now owns its completion state. A send that reached its deadline left the uAMQP operation in flight, because a cancel can make uAMQP call the completion handler two times. That operation later pushed its result into the one queue that the sender shared between every send, so the next send took the stale result as its own. A retry could thus report the outcome of the attempt before it. [#7254]
- The uAMQP receiver now registers its connection waiter under the connection lock. That waiter reads the saved error of the link on the calling thread, while the polling thread writes the same field, so the read was a data race. The sender already used this lock. [#7254]
- A cancelled caller no longer stops a teardown on the Rust transport. A close took the time that remained for the caller as its bound. A cancelled context leaves no time, so the bound was zero and every close failed at once with an
Elapsederror. The caller then stopped before it closed the connection, and the destructor of that connection stopped the process. A teardown now takes the default bound when the deadline of the caller already passed. It keeps a caller deadline that has time left. [#7254] - The uAMQP sender now holds every send that waits, not one send at a time.
ProducerClient::Sendlets sends to one partition run together, so a second send replaced the first in the old single slot. uAMQP drains each in-flight send before it reports the error state, so no send was stranded in practice. This change keeps the error wake correct if that order changes. [#7254] -
ServiceBusSasConnectionStringCredentialno longer throws when the connection stringEntityPathand the entity path argument differ only by ASCII letter case. The comparison folds ASCII A-Z only. It is not the culture-awareInvariantCultureIgnoreCasecomparison that .NET uses.GetEntityPath()returns the connection string spelling. .NET returns the explicit argument instead, so this behavior deviates from .NET. [#7261] - A claims based security open that fails now names the result, the audience, and the function that asked for the token. The exception said “Could not open Claims Based Security object.” and nothing more, so a reader could not tell which audience failed or whether the failure came from the first authentication or from the token refresh. The connection also writes a warning that adds the token type and the token expiry. Neither the exception nor the warning holds the token.
- The claims based security warning also names the connection instance, the host, the connection state, and the time that the open took. A reader can now separate a client fault from a service fault: an open that fails in about no time, on a connection that is already in the
Endstate, never reached the service, and a repeated instance number shows that the client did not build a new connection. The connection state is now atomic, because the uAMQP polling thread writes it while another thread reads it for this line. - The uAMQP connection now names the reason that the service gives when it closes the connection. uAMQP reads that reason from the
closeperformative and offers it through one subscription, and the connection did not take that subscription, so the reason reached no log. A client that the service closed after an idle period saw only the failures that followed. A transport I/O error now writes a separate warning with the connection instance, host, and state. The uAMQP callback provides no error payload, so the warning says that no details are available. - The uAMQP message sender and message receiver now write the detach condition and the description each time the service detaches a link, and the line no longer needs the AMQP trace option. Each one also writes the line for a detach that arrives during the open. Before, that detach was silent, and the open reported a generic error that did not name the condition. The
$cbslinks take that path when the service rejects the attach. - A claims based security open that fails now throws
CbsOpenFailedException, which carries theCbsOpenResult. The three failures need different handling:Errorreached the transport and may be retried, whileCancelledis the caller’s own cancellation or deadline andInvalidis a state error. The result was previously readable only by matching the message text, so a reword would have changed caller behavior with no compiler error. The type derives fromstd::runtime_errorand carries the same message, so existing handlers keep working. The Rust backend reports every open failure by throwing rather than by returning a result, so those throws are classified at the shared call site and carry the same type. - The uAMQP management client now closes the message sender when the message receiver fails to open. Two handlers returned a status without that close, and a message sender that stays open stops the process in its own destructor.
- The uAMQP management client now names the management node and the open status in the lines that it writes when an open fails, and it keeps the text of the exception that ended the open. The message sender open failure moved from the Error level to the Warning level, because that call reports the failure to its caller.
- A claims based security open that fails now carries the reason that the layer below reported.
CbsOpenResult::Errorcovers every transport, TLS and link failure, so a reader holding only the result could not separate a refused socket from a rejected attach. The management client wrote that reason to the log and then dropped it, becauseManagementClientImpl::Openreports a failure as a status and the exception that named the cause was destroyed in the handler. The reason now travels with the status and reaches both the warning and theCbsOpenFailedExceptionmessage, so a caller that logs the exception and has no log listener can still tell what failed. It names which of the two links failed, because the sender and the receiver fail for different causes. The reason is empty when the layer below gave none, and the sentence then reads exactly as it did before. It never holds the token. - The claims based security object now keeps the AMQP error that the service sent.
ClaimsBasedSecurityImpl::OnErrorreceives the condition, the description, and the info map, which is the richest statement the service makes about a refused claim, and it only wrote them to the log. They are now added to the reason that the open failure carries. The capture takes a lock of its own, because that callback runs on the polling thread while the management client holds its open lock.
Azure Blob Storage Checkpoint Store 1.0.0-beta.4 Changelog
Other Changes
- No public changes in this release.
Event Hubs 1.0.0-beta.14 Changelog
Features Added
-
[#7250] Restored connection-string authentication for
ProducerClientandConsumerClient, including support for the Event Hubs emulator. -
[#7295] Connection-string authentication now works on the Rust AMQP backend.
ProducerClientandConsumerClientno longer throw when the caller passes a connection string. -
[#7254]
ProducerClient::Sendnow builds a new sender on each retry attempt. A failed attempt discards the sender, the session, and the connection for that partition, so the next attempt builds all three again and authenticates with a current token. A send that a link detach ended previously failed for the life of the client. -
[#7254]
PartitionClient::ReceiveEventsnow attaches a new receiver after a link fault, and it starts after the last event that it gave the caller. So the caller sees no duplicate event and no lost event. A permanent condition, for exampleamqp:link:stolen, still reaches the caller at once. A call that already holds events gives them back and recovers on the next call.
Bugs Fixed
-
[#7254] A teardown of the cached sender no longer runs while another thread sends on that sender.
ProducerClient::Sendgives each attempt a copy of the sender, and a failed attempt on one thread closed the object that a second thread was using. On the Rust AMQP backend that close frees the sender, so the race was a use after free. Each partition now has a guard that lets sends run at the same time and makes a teardown wait for the sends in flight.ProducerClient::Closeuses the same guard, and it now logs a failed close and continues instead of leaving the other objects open. -
[#7254]
ProducerClient::CreateBatchnow builds a new sender when it cannot read the maximum message size. The client caches a sender for each partition, and a cached sender holds a link that the service detaches after 30 idle minutes. The size of a batch comes from the attached link, so this call was the first one to touch the dead link, and it threw. TheSend(EventData)overloads go through this call, so the whole producer failed after an idle period even thoughSendbuilds a new sender on each attempt. -
[#7254] Updated producer retries to honor
EventHubsException::IsTransient, treat empty AMQP error conditions as transient, stop immediately for unknown and known non-transient failures, preserve bounded retries for AMQP runtime failures, and make backoff cancellable throughAzure::Core::Context. Retry accounting now always performs the initial attempt and treatsMaxRetriesas additional retry attempts. -
[#7335] The internal properties client can no longer end the process through its destructor.
EventHubsPropertiesClient::Closemarked the client closed only afterManagementClient::Closereturned, so a close that threw left the client marked open, and it also skipped the session end. The destructor then closed the same client a second time with no try block, and a destructor isnoexcept, so that second throw would callstd::terminate.Closenow marks the client closed before the call, gives each step its own try block, and ends the session on the failing path too. The destructor catches and logs. This is a guard against a throwing close, and not a fix for an observed crash: a live test that left a management link idle for 35 minutes did not make the uAMQP close throw. Release 1.0.0-beta.11 records the same rule in [#6957]. -
[#7257] Fixed the partition key on a batch envelope.
EventDataBatch::ToAmqpMessagewrote thex-opt-partition-keyvalue to the AMQP delivery-annotations section. The Event Hubs service ignores that section. The batch also built the envelope from the message before the code applied the partition key annotation. A batch with a partition key thus spread across all partitions. The partition key now goes in the message-annotations section, on the batch envelope and on each message in the batch. A batch that setsEventDataBatchOptions::PartitionKeynow lands on one partition. -
[#7254]
ProducerClient::Sendno longer waits forever when the connection goes away. A caller that gives no deadline now gets a bound of 60 seconds on the send, and the client treats that timeout as transient, so the next attempt builds a new sender. A close of the sender or of the receiver also comes back when the connection is gone, instead of waiting for a detach that never arrives, and the attach that a rebuild runs next carries the same bound.PartitionClient::ReceiveEventskeeps the deadline of the caller, because a receive is a long poll that the caller controls. -
[#7254]
Processor::CloseandConsumerClient::Closenow finish closing every partition after one AMQP teardown reports a lost connection. A receiver detach can reportIllegalSessionStateafter the service resets its connection. That error stopped the cleanup loops, left other connections open, and could end the process when those open connections were destroyed.
Event Hubs 1.0.0-beta.15 Changelog
Bugs Fixed
-
[#7389]
ProducerClientnow tests a cached sender for liveness before it uses one. Holding a sender in the map is not the same as holding a usable one: a link that the service detached during an idle period stayed cached, and the read of the maximum message size inCreateBatchwas the first call to touch it, so that read threw and the exception was how the client learned the link had died. The producer now discards a detached stack before use, and the size read no longer depends on a live link. A rebuild after an idle close costs the same connection, TLS and claims based security handshake as before; what this removes is the exception and its warnings on that path. This change applies to the uAMQP transport. -
[#7389] The claims based security retry in
ProducerClient::CreateBatchnow waits a short random interval before its one further attempt, instead of making it in the same instant. Every producer in a process that failed together went back into whatever caused the first failure together. The wait is a uniform value between zero and 100 milliseconds, drawn from a generator seeded for each thread. It is deliberately not the configuredRetryDelayand not a backoff step: the purpose is to spread producers that failed at the same moment, not to wait for a condition to clear, so it does not add the best part of a second to a path that is already rebuilding a connection, and it does not depend onRetryOptions. The bound stays one retry, and only a claims based security open that reportedCbsOpenResult::Erroris retried;CancelledandInvalidstill reach the caller on the first attempt.
Latest Releases
View all the latest versions of C++ packages here.
Installation Instructions
To install the packages, copy and paste the following commands into a terminal:
$> vcpkg add port azure-core-amqp-cpp
$> vcpkg add port azure-messaging-eventhubs-cpp
$> vcpkg add port azure-messaging-eventhubs-checkpointstore-blob-cpp
You can also install the packages from source:
# From Source
git clone https://github.com/Azure/azure-sdk-for-cpp
# git checkout <tag_name>
# For example:
git checkout azure-storage-blobs_12.0.0
Feedback
If you have a bug or feature request for one of the libraries, please post an issue to GitHub.