Skip to content

[Service Bus] Fix spurious DeliveryNotOnLinkException in session processor (#47356)#49603

Open
ksalazar-91 wants to merge 2 commits into
Azure:mainfrom
ksalazar-91:ksalazar-91/servicebus-session-double-settle-47356
Open

[Service Bus] Fix spurious DeliveryNotOnLinkException in session processor (#47356)#49603
ksalazar-91 wants to merge 2 commits into
Azure:mainfrom
ksalazar-91:ksalazar-91/servicebus-session-double-settle-47356

Conversation

@ksalazar-91

Copy link
Copy Markdown

Summary

Fixes #47356.

A session-enabled ServiceBusProcessorClient (PEEK_LOCK) that settles messages manually inside the handler — e.g. calling context.complete()while auto-complete is left enabled (the default) intermittently logged a spurious error at ERROR:

c.a.c.a.implementation.handler.ReceiveLinkHandler2 - {"az.sdk.message":"Cannot process the
disposition request to set the state as 'Accepted{}' for the delivery with delivery tag (id)
'...'. Reason: The delivery with the delivery tag does not exist in the link's DeliveryMap.", ...}

The message is already settled correctly on the broker; the only symptom is this misleading DeliveryNotOnLinkException log line (one per message). No messages are lost or redelivered, and processError is not invoked.

Root cause

The processor's auto-disposition feature settles a message after the handler returns. When the handler also settles manually, two dispositions are issued for the same delivery. The SDK is designed to absorb this redundant settle via the message.isSettled() guard in updateDisposition:

} else if (message.isSettled()) {
    return Mono.error(new IllegalArgumentException("The message has either been deleted or already settled."));
}

The non-session and V1-session paths arm this guard by calling message.setIsSettled() on a successful disposition (ServiceBusReceiverAsyncClient). The V2 session path (SessionsMessagePump.SessionReceiversTracker.updateDisposition) never did. So the flag stayed false, the guard could not trip, and the redundant auto-complete proceeded to a second link-level disposition on a delivery that had already been removed from the DeliveryMap — producing the DeliveryNotOnLinkException ERROR.

Fix

Mark the message settled on a successful disposition in the V2 session path, mirroring the sibling paths:

updateDispositionMono = receiver.updateDisposition(message.getLockToken(), deliveryState)
    .doOnSuccess(__ -> message.setIsSettled());

This arms the existing isSettled() guard so a redundant settle short-circuits with the benign "already settled" error (swallowed at verbose by the auto-disposition wrapper) instead of reaching the receive-link. The change is package-local to azure-messaging-servicebus; no azure-core-amqp change is needed.

Verification

Reproduced live against a Standard namespace with a session-enabled subscription, sending 5 messages to one session and a session processor that calls complete() manually with auto-complete enabled.

Version disableAutoComplete() Messages settled processError DeliveryNotOnLinkException logged
7.17.16 (reported) off 5/5 0 yes
7.17.18 (latest, amqp 2.11.4) off 5/5 0 yes
This PR (7.18.0-beta.2) off 5/5 0 no
This PR (7.18.0-beta.2) on 5/5 0 no ✅

The fix removes the error log with auto-complete still enabled (no customer config change required) and does not regress the manual-settlement path.

Notes for reviewers

  • Using disableAutoComplete() remains the recommended pattern when settling manually; this PR additionally makes the SDK fail quietly and consistently on a redundant settle for session processors, matching the documented "auto-settle is tolerated" behavior already present in the other receive paths.
  • CHANGELOG updated under 7.18.0-beta.2 (Unreleased) → Bugs Fixed.

…essor (Azure#47356)

A session-enabled ServiceBusProcessorClient that settles messages manually
(e.g. complete()) while auto-complete is left enabled logged a spurious
DeliveryNotOnLinkException ("...does not exist in the link's DeliveryMap") at
ERROR. The V2 session disposition path (SessionsMessagePump) never called
message.setIsSettled() on success, so the isSettled() guard could not absorb the
redundant auto-settlement the way the non-session and V1 session paths do. The
second disposition then reached the receive-link after the delivery had already
been removed from the DeliveryMap, producing the error log.

The message was always settled correctly on the broker; only the misleading
ERROR log is removed. Now the session path marks the message settled on a
successful disposition, mirroring ServiceBusReceiverAsyncClient, so a redundant
settle short-circuits at the already-settled guard.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 23, 2026 05:48
@github-actions github-actions Bot added Community Contribution Community members are working on the issue customer-reported Issues that are reported by GitHub users external to the Azure organization. Service Bus labels Jun 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your contribution @ksalazar-91! We will review the pull request and get back to you soon.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a misleading DeliveryNotOnLinkException ERROR log that can occur in session-enabled ServiceBusProcessorClient when a message is settled manually inside the handler while auto-complete is still enabled, by ensuring the V2 session disposition path marks messages as settled after a successful disposition.

Changes:

  • Mark ServiceBusReceivedMessage as settled (message.setIsSettled()) after a successful disposition in the V2 session path to activate the existing message.isSettled() guard on redundant settlements.
  • Add a release note describing the removed spurious error log and clarifying that broker settlement was already succeeding.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/SessionsMessagePump.java Marks messages as settled on successful session disposition to prevent redundant settle attempts from reaching the receive-link.
sdk/servicebus/azure-messaging-servicebus/CHANGELOG.md Documents the bug fix in 7.18.0-beta.2 (Unreleased) under “Bugs Fixed”.

Comment on lines +904 to +905
updateDispositionMono = receiver.updateDisposition(message.getLockToken(), deliveryState)
.doOnSuccess(__ -> message.setIsSettled());

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a regression test in the latest push (commit 5e9b2ff): SessionsMessagePumpIsolatedTest#shouldNotReDispositionWhenHandlerSettlesWithAutoCompleteEnabled. It runs a session message through the pump with auto-complete enabled and a handler that calls complete() manually, then asserts (1) message.isSettled() becomes true (setIsSettled() invoked once), and (2) the receive-link sees exactly one updateDisposition call — proving the redundant auto-settlement short-circuits at the isSettled() guard and never issues a second disposition that would raise DeliveryNotOnLinkException. Verified passing (3/3 with related disposition tests); checkstyle + spotbugs clean.

)

Adds SessionsMessagePumpIsolatedTest#shouldNotReDispositionWhenHandlerSettlesWithAutoCompleteEnabled,
covering the V2 session path where a handler settles a message manually while auto-complete is
enabled. Verifies the message is marked settled and the redundant auto-settlement short-circuits at
the isSettled() guard so the receive-link sees exactly one disposition (no second attempt that would
raise DeliveryNotOnLinkException). Addresses the PR review feedback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Community Contribution Community members are working on the issue customer-reported Issues that are reported by GitHub users external to the Azure organization. Service Bus

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]DeliveryNotOnLinkException Race Condition in Session-Enabled Processor (PEEK_LOCK Mode)

2 participants