Logic App Standard Stateful Service Bus Trigger (Peek-Lock + Split-On + Workflow Concurrency) intermittently fails to instantiate workflow runs for received messages. Messages reach MaxDeliveryCount and DLQ without any corresponding workflow failure

Abhishake Jaiswal 160 Reputation points
2026-07-23T01:27:20.6866667+00:00

I have a logic app standard with Service bus trigger peek lock with split on enabled and workflow concurrency enabled.

We noticed that messages are going to dead letter after 3 retries but the WF never gets triggered. There are no Workflow failures.
the Message lock time is set to 5 min in service bus topic.

and when we retry the same message from azure portal service bus explorer , we get similar behaviour. the WF never triggers.

and a dead letter count keeps on increasing.
any pointers what could be wrong here ?

Azure Logic Apps
Azure Logic Apps

An Azure service that automates the access and use of data across clouds without writing code.


2 answers

Sort by: Most helpful
  1. Pravallika KV 18,850 Reputation points Microsoft External Staff Moderator
    2026-07-23T09:14:02.5833333+00:00

    Hi @Abhishake Jaiswal ,

    If you're seeing messages land in the dead-letter queue with no corresponding workflow run and no visible failure and it reproduces deterministically on a single message at zero load this is almost always a Split-On debatching issue, not a lock/timeout problem.

    The Standard peek-lock Service Bus trigger relies on the workflow run to settle the message (Complete or Abandon). When you enable Split-On (@triggerOutputs()?['body']), the trigger debatches the array and creates one run per item.

    If, for a given message, that expression doesn't resolve to a valid, non-empty array i.e. it's an empty array, a single object / non-array, or a shape that doesn't match under the batched (concurrency-on) output then no run is created for that message. And because no run owns the message:

    • Nothing calls Complete or Abandon → the peek-lock silently lapses.
    • DeliveryCount increments on each redelivery.
    • After MaxDeliveryCount (default 3), the message is dead-lettered with DeadLetterReason = MaxDeliveryCountExceeded.

    No run ever executed, so there's no workflow failure to see. That's why it reproduces every time on one specific message the message's own content is what makes Split-On yield nothing.

    Why enabling concurrency triggered it:

    Two documented behaviors reinforce this:

    • Turning trigger concurrency on reduces the Split-On limit from 100,000 to 100 items, and "if the number of items exceeds this limit, the Split on capability is disabled." (Limits reference)
    • The Service Bus trigger is documented to route messages to the DLQ under concurrency + batching edge cases. (Service Bus connector considerations)

    So the typical history matches:

    • Concurrency off → Split-On limit 100,000, every message got a run (often CPU-saturating), everything settled → no DLQ.
    • Concurrency on → Split-On limit dropped to 100 and the batched output shape changed → some messages debatch to nothing → silent DLQ.
    • Raising concurrency/waiting-run values only helped the load-induced portion; the deterministic DLQs are debatch failures that tuning can't fix.

    Follow below steps to confirm:

    1. Service Bus Explorer → Peek Mode → the DeadLetter subqueue (non-destructive). On a dead-lettered message, check DeadLetterReason = MaxDeliveryCountExceeded and DeliveryCount = 3, then inspect the message body the failing ones will be an empty / non-array / differently-shaped payload compared to the ones that succeed.
    2. Metrics (filter EntityName = your subscription, aggregation = Sum): during failures you'll see Incoming rises, Dead-lettered rises, but Completed stays flat messages arrive and dead-letter without ever being completed. Quick count check:
    az servicebus topic subscription show \
      --resource-group <rg> --namespace-name <ns> \
      --topic-name <topic> --name <sub> --query countDetails
    

    Fix:

    Turn Split-On off and debatch inside the workflow using a For each over @triggerBody(). A real run is always created, so the message is always settled an empty or odd payload just results in a run that does nothing, instead of silently dead-lettering.

    Additional notes:

    • If you keep Split-On, make sure no debatched array exceeds 100 items while concurrency is on, otherwise Split-On is disabled for that batch.
    • Always use the ? operator (@triggerOutputs()?['body']) so a missing property doesn't hard-fail.
    • Trigger concurrency is irreversible once enabled plan accordingly.
    • If high throughput is the goal, consider a Recurrence trigger + "Get messages" action pattern instead, where you control the pull count and settlement explicitly (Recurrence also avoids waiting-run/lock-expiry issues). Hope this helps!

    If the resolution was helpful, kindly take a moment to click on User's imageand click on Yes for was this answer helpful. And, if you have any further query do let us know.

    Was this answer helpful?

    0 comments No comments

  2. Christos Panagiotidis 3,546 Reputation points
    2026-07-23T05:35:29.7766667+00:00

    The fact that no run is created is important: the failure is in the trigger or host path before the workflow instance starts, so it will not appear as a failed run. Check trigger history, Logic App host logs, and the Service Bus dead-letter reason using the same message ID and timestamps. With Peek-Lock, Split On, and workflow concurrency enabled together, keep the trigger batch size within the Split On concurrency limit and leave enough waiting-run capacity; otherwise, deliveries can retry while no run is admitted. Temporarily disable concurrency or Split On, set a small batch, and retest with the V2 Service Bus trigger. Confirm the lock duration or renewal window exceeds processing time and the connection has Listen permission. If the host still abandons messages before creating runs, collect message IDs, trigger-history entries, and runtime logs for Microsoft Support; only platform-side logs can show the pre-run exception.

    Was this answer helpful?

    0 comments No comments

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.