fix(core-amqp): carry the cause of a CBS open failure to the caller - #7384
Merged
Johnathan W (j7nw4r) merged 1 commit intoAug 29, 2026
Merged
Conversation
|
Azure Pipelines: Successfully started running 2 pipeline(s). 8 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
There was a problem hiding this comment.
Pull request overview
Propagates detailed CBS open-failure causes through the AMQP stack to logs and exceptions.
Changes:
- Carries management/link failure details alongside open status.
- Preserves service detach errors and prevents stranded opens.
- Adds failure-message tests and changelog entries.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
CHANGELOG.md |
Documents improved CBS diagnostics. |
src/amqp/connection.cpp |
Adds failure details to logs and exceptions. |
src/amqp/private/cbs_open_failure.hpp |
Formats optional failure details. |
src/impl/uamqp/amqp/claim_based_security.cpp |
Collects management and service errors. |
src/impl/uamqp/amqp/private/claims_based_security_impl.hpp |
Adds synchronized error storage. |
src/impl/uamqp/amqp/management.cpp |
Propagates link-specific open failures. |
src/impl/uamqp/amqp/private/management_impl.hpp |
Stores and queues failure details. |
src/impl/uamqp/amqp/message_sender.cpp |
Preserves detach errors during open. |
src/impl/uamqp/amqp/private/message_sender_impl.hpp |
Adds open-specific detach storage. |
src/impl/rust_amqp/amqp/claim_based_security.cpp |
Implements backend-compatible detail accessor. |
src/impl/rust_amqp/amqp/private/claims_based_security_impl.hpp |
Declares detail accessor. |
test/ut/claim_based_security_tests.cpp |
Extends CBS failure assertions. |
test/ut/connection_tests.cpp |
Tests detailed failure formatting. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
Sagar Patel (sagar0207)
force-pushed
the
fix/cbs-open-failure-root-cause
branch
from
August 28, 2026 21:49
d695bb2 to
6260228
Compare
A claims based security open reports every transport, TLS and link failure as `CbsOpenResult::Error`, so a caller holding only that result cannot tell a refused socket from a rejected attach. The reason existed at each layer and was written to the log, then dropped: `ManagementClientImpl::Open` reports a failure as a status, and the exception that named the cause was destroyed in its handler. A caller that logs the exception and runs no log listener therefore had nothing to read. The reason now travels with the status and reaches the `CbsOpenFailedException` message. It names which of the two links failed, because the sender and the receiver fail for different causes, and the shape of the reason separates the layers: a reason that says the open threw never reached the service, while a reason that carries an AMQP condition means the service answered and refused. `ClaimsBasedSecurityImpl::OnError` receives the condition, the description and the info map that the service sent, which is its richest statement about a refused claim, and only wrote them to the log. They now reach the reason as well. That capture takes a lock of its own, because the callback runs on the polling thread while the management client holds its open lock. The open complete queue carries the reason with the status, so the handlers that complete it from the polling thread never take the lock that `Open` holds. This change is diagnostics only. It alters no control flow and no state machine. No message holds the token. Copilot-Session: 7ae0b8be-ee18-4b04-8ddf-158d6ffbd1c9
Sagar Patel (sagar0207)
force-pushed
the
fix/cbs-open-failure-root-cause
branch
from
August 28, 2026 22:11
6260228 to
9162891
Compare
Sagar Patel (sagar0207)
marked this pull request as ready for review
August 28, 2026 23:36
|
Azure Pipelines: Successfully started running 2 pipeline(s). 8 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Johnathan W (j7nw4r)
enabled auto-merge (squash)
August 29, 2026 12:13
Johnathan W (j7nw4r)
approved these changes
Aug 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A claims based security open reports every transport, TLS and link failure as
CbsOpenResult::Error. A caller holding only that result cannot tell a refused socket from a rejected attach.The reason existed at each layer and was written to the log, then dropped.
ManagementClientImpl::Openreports a failure as a status, so the handler that caught the exception loggede.what()and returned an enum:A caller that logs the exception and runs no log listener therefore read only:
What this changes
The reason now travels with the status and reaches the
CbsOpenFailedExceptionmessage, so it is visible to a caller that never enables the SDK logger. It names which of the two links failed, because the sender and the receiver fail for different causes.ClaimsBasedSecurityImpl::OnErrorreceives the condition, the description and the info map the service sent, which is its richest statement about a refused claim, and only wrote them to the log. Those now reach the reason as well.This change is diagnostics only. It alters no control flow and no state machine.
Result
Captured from the live failure test, verbatim:
The shape of the reason separates the layers. A reason that says the open threw never reached the service. A reason that carries an AMQP condition means the service answered and refused. That distinction is the one a caller currently cannot make at all.
Thread safety
OnErrorruns on the polling thread whileManagementClientImpl::Openholds its open lock acrossWaitForResult, so that capture takes a lock of its own; taking the management open lock there would block the polling thread for the whole open.For the same reason the open complete queue carries the reason with the status (
AsyncOperationQueue<ManagementOpenStatus, std::string>) rather than a member the completing handler would have to lock.Compatibility
detailis a defaulted parameter on both text builders, so existing call sites are unchanged and an empty reason produces byte identical text.ManagementClientImplis only a complete type inside the library, so no exported layout changes. No message holds the token; the$cbsclient never callsAuthenticateAudienceand both of its links setAuthenticationRequired = false.Validation
azure-core-amqp-tests: 228 / 230 pass, which matches the baseline on this commit's parent exactly.TestCbs.CbsOpenNoListener,TestMessageSendReceive.SenderCloseWhileUnsettledSendIgnoresLateDisposition) reproduce on cleanmainat9dacd081with none of this change applied. They are pre-existing and are not addressed here.TestCbsOpenFailureTextfor the reason in the exception text and in the log line, and three assertions added toTestCbs.AuthenticationFailureNamesTheCbsOpenFailureconfirming the reason reaches a real failure, names the failing link, and never carries the generic state error. The existing sentinel token assertions still confirm no credential reaches any message or log line.Deliberately not included
Two further changes were prototyped and withdrawn from this PR because they could not be covered by a regression test, and both are behavioural rather than diagnostic:
Preferring a detach condition seen while the sender is still opening. When the service rejects a link during the open,
m_senderOpenis still false, soOnLinkDetacheddiscards the error and the open reportsamqp:internal-errorwith "Message Sender entered the Error State." Attempts to reproduce this against the mock failed: a bareDETACHis not routed to the client's detach handler because the link never attached, and sendingATTACHthenDETACHfrom insideOnLinkAttachedaborts with "Failed to send detach performative".Completing the open when a
$cbslink is disconnected while the management client is opening. Today the disconnect handlers move the client to the error state, the link's own transition to that state is then ignored, and nothing completes the open, soOpenwaits for the caller's context to be cancelled and reportsCancelledrather thanError. Because onlyErroris retried, that also disables the retry. Reproducing it needs the mock to hold or delay a link attach, which it cannot currently do — the open completes as soon as both$cbslinks attach, so there is no window to act in.Both are worth doing. They need a delayed-attach capability in
AmqpServerMockfirst, and they belong in a PR that can demonstrate them.