Agent speaks the transfer announcement but never executes the transfer tool

Hi Retell team,

We run agents (gpt-4.1) that transfer callers via a custom webhook tool getTransferPhone followed by the native transfer tool transferToLiveAgent.

In roughly 1 in 100 transfer attempts, the agent gets the getTransferPhone result and speaks the transfer announcement (“Please hold while I transfer your call…”) but the transfer_call tool never executes. The agent then acts as if the transfer were in progress — keeps telling the caller to stay on the line, asks “are you still there?”, and eventually ends the call without ever transferring.

Example call IDs:

  • call_39cea54de6abd6c798f8f15d564
  • call_3d23201114b154950bbb6e90b37
  • call_c125980616bde10d5a2b4313a1e
  • call_be6109f9140bff06334fa6f78f8

We replayed these exact turns (same prompt, history, and tools as far as we know) directly against gpt-4.1 as tests, and the model calls the transfer tool every single time — so we can’t reproduce this outside Retell. It seems we are not getting the correct llm interaction in order to reproduce the scenario. Since this is 1 in 100 occurance we run the tests many times.

Any guidance on reproducing this deterministically? We’d like to build a regression test we can run before/after any mitigation we do on the prompts.

Thanks!

Hello @pkostadinov

I have escalated your issue to the relevant team. As soon as I receive any updates from them, I will make sure to keep you informed.

Best Regards.

Hello @pkostadinov

It looks like you’re using a two-step transfer flow: first to retrieve the phone number and then to transfer the call. The issue is that the agent cannot call both functions consecutively without a user turn in between.

You can resolve this by prompting the user to confirm the transfer after the first function completes, then proceeding with the transfer function. Alternatively, you can simplify the flow to a single step by retrieving the phone number through the inbound webhook before the call starts.

Best Regards.

Hi, thanks for looking at this — but the diagnosis doesn’t match what we see in the call logs.

The two-step flow works in the overwhelming majority of calls. Over the last 90 days roughly 220,000 calls completed both functions back-to-back, and the failure rate is about 1 in 100. If the agent couldn’t call both functions consecutively without a user turn in between, none of these would have worked. Here are a few successful examples on the same agent as the failing calls, where getTransferPhone is followed directly by transferToLiveAgent with no user turn between them: call_658a7ed1ccd9931e8d1e07051e5, call_4757ff971f4c629604a2b1a873e, call_13022c6a1f9552ae73658995acc.

We also replay the failing turns in our test harness directly against gpt-4.1 — same prompt, same conversation history including the getTransferPhone result, same tools — including the variant where a customer turn lands between the phone retrieval and the transfer. Across thousands of runs the model calls the transfer function every single time with and without customer turn in between. So the model reliably produces the transfer call given this flow; in the rare failing calls something between the model output and the tool execution appears to drop it.

The inbound-webhook suggestion unfortunately doesn’t fit our case: the transfer destination depends on the transfer reason (which department or intent the call resolved to), and that’s only known mid-conversation at the moment of transfer — that’s exactly why the number is retrieved with a function at that point rather than up front.

What would really help: could you check the interaction logs for the failing calls (call_39cea54de6abd6c798f8f15d564, call_3d23201114b154950bbb6e90b37, call_c125980616bde10d5a2b4313a1e, call_be6109f9140bff06334fa6f78f8) at the turn right after the getTransferPhone result? Specifically whether the LLM emitted a transfer_call invocation that was discarded — for example because the caller spoke during the transfer announcement — or never emitted one at all. We’ve noticed the failures correlate with the caller backchanneling (“Okay”, “Yes”) while the agent speaks the announcement. That’s the piece we can’t observe from outside, and it’s what we need to build a reproducible test and then experiment with fixes.

Best Regards.

Hello @pkostadinov

I have escalated your issue to the relevant team.

Best Regard.

Hello @pkostadinov

This hiccup occurs because the LLM sometimes attempts to call the second function but fails internally to generate the response after it. I recommend splitting it into two separate steps: first, inform the user you are checking if someone is available to take the call; then, notify them that you are transferring. For a better solution, use conversation flow to control the steps and call the function deterministically every time.

Best Regards.

Hi, thanks — this is actually the most useful piece of information so far, and I’d like to confirm we’re reading it correctly.

You’re saying the LLM does attempt the transferToLiveAgent call in these cases, and the failure happens internally when generating the response after it. Two questions on that:

First, that attempted invocation is not visible anywhere we can see — not in the transcript, not in transcript_with_tool_calls, and our webhook never receives anything for the turn. If the invocation is attempted and then dropped, could you confirm that’s what your internal logs show for the calls we referenced (e.g. call_3d23201114b154950bbb6e90b37)? And is there any place this failure mode is surfaced to us — call analysis, webhook event, anything — so we can at least detect it when it happens?

Second, and more importantly: why does executing the transfer depend on a response being generated after the function call? From our side, once the model has emitted the transfer_call invocation, we’d expect the transfer to execute regardless of whether a follow-up utterance gets generated — the tool itself already has speak_during_execution with an execution message for exactly that purpose. If a post-invocation generation failure cancels the tool execution, that seems like the actual bug here: the caller ends up announced-but-never-transferred and the agent continues as if the transfer were in progress. Is committing the tool execution before (or independently of) the follow-up generation something you can fix or that we can configure?

On the workarounds: splitting the announcement into two spoken steps doesn’t change anything about the invocation being dropped, so we don’t expect it to help. Regarding conversation flow — does the transition node execute the transfer through a different mechanism than retell-llm function calling, i.e. is it actually immune to this internal failure? If yes, that’s a meaningful argument for migrating; if it rides on the same mechanism, it isn’t.

We’re happy to provide more failing call IDs if it helps — we have around 40 confirmed examples over the last 90 days.

Thanks!

Hello @pkostadinov

Can you share 2-5 call ids to check.

Best Regards.

Sure — here are ten failed-transfer calls across different call scenarios. In every one of them getTransferPhone returns a number, the agent speaks the transfer announcement, and transferToLiveAgent never executes:

  • call_d6b957b2965090a7aafca56f603 — reschedule not possible in system, transfer announced (Aug 10)
  • call_bed051a0a8b9d5f33a7d59661c6 — cancellation requires transfer (Aug 10)
  • call_818d407c61553a0d249ea5b9129 — customer needs same-day/sooner service (Aug 11)
  • call_7a47e118614f3a9e3818ea1cdfc — existing-job question, not a new service request (Aug 10)
  • call_066d1ac41b035fbb03ad29f8992 — appointment not found, transfer to a human (Aug 8)
  • call_1aa2ddf38d371dd95233e6efdd6 — customer explicitly asked for a human (Aug 3)
  • call_0f2e63702cc4f2439620c8c64df — no acceptable appointment slot available (Aug 3)
  • call_e16d69509658e5bd634120aebcb — scheduling cutoff triggered a transfer (Jul 24)
  • call_af098ae9c7d3830ea18c85815bd — transfer after a tool error (Jul 21)
  • call_64bdcd1a6b749711bdcfa2731b7 — caller outside service area (Jun 30)

These span several different agents (including multi-prompt agents after an agent_swap and single/entry agents with no swap) and both customer-requested and system-triggered transfers.

Thanks again for taking the time to help us with this.

Hello @pkostadinov

I have escalated your request to the relevant team. As soon as I receive any updates, I’ll let you know.

Best Regards.

Hi @mark1,

Thanks for escalating on the 14th. It’s been two weeks since then, so I wanted to check whether the relevant team has come back with anything — even a “still looking” or “we can’t pull those logs” would help us plan.

Happy to jump on a short call or share any account/agent details that would speed this up. If the interaction logs simply aren’t retrievable, please just say so and we’ll design a mitigation blind rather than keep waiting.

Best Regards.

Hey @pkostadinov

Some fixes were pushed recently. Could you please check if the issue is still occurring this week? If it is, please share the relevant Call IDs so we can investigate further.

Best Regards

Hi Mark, we re-measured after your Aug 28 note. Nothing changed.

Aug 28 - Sep 2 (UTC), production, all agents:

  • calls that invoked getTransferPhone: 20,227 (20,323 invocations)
  • getTransferPhone followed immediately by transferToLiveAgent: 20,145
  • no transferToLiveAgent anywhere later in the call: 153 (0.8%), same rate as the May-Aug baseline

We also went through the public call logs and word timestamps, comparing 40 successful transfers with the failing calls. What we see:

  1. transferToLiveAgent is deferred until the agent finishes speaking. In 32 of 39 successful transfers “Calling tool: transferToLiveAgent” is logged within 100 ms of the last spoken word, 1-22 s after the LLM request that produced it. So a long announcement is a 10-20 s window in which the pending transfer can be lost.

  2. Failure pattern A (e.g. call_39cea54de6abd6c798f8f15d564, call_3d23201114b154950bbb6e90b37, call_c125980616bde10d5a2b4313a1e): the customer says “Okay”/“All right” during the announcement, the agent transcript is cut mid-sentence, the log shows the LLM request right after the getTransferPhone result and then nothing until the customer speaks again. No transferToLiveAgent is ever logged.

  3. Failure pattern B (call_be6109f9140bff06334fa6f78f8, call_015eda7db3fa22872faf2a4c8e3, call_9e5c26f5200753492230575e1da): the agent finishes the announcement and speaks the transfer tool’s execution_message (“One moment.” / “Connecting you now.”), which only exists as an argument of the transferToLiveAgent call, yet “Calling tool: transferToLiveAgent” is not logged and no transfer happens. In call_015eda7db3fa22872faf2a4c8e3 the customer says “K.” overlapping the final word.

Both patterns look like the deferred transfer being cancelled by user speech around the end of the agent utterance, which matches your Aug 13 comment that the LLM calls the function but the response after it fails internally.

Questions:

  • Did the Aug 28 fix target this path (transfer_call pending while the agent is speaking, then user speech arrives)? Our numbers say it did not reach production behaviour.
  • Can you confirm from your side, for the pattern B calls above, that the transfer_call tool call was received from the LLM?
  • Is there a way to make transfer_call execute immediately (or survive a barge-in) instead of waiting for TTS to finish?

Those numbers make this especially useful as a regression case because you already have a measurable baseline rather than just anecdotal failures.

I’d separate the transfer path into explicit states:

  • transfer intent reached
  • destination resolved
  • transfer announcement started
  • announcement completed/interrupted
  • transferToLiveAgent invocation observed
  • destination connection observed
  • intended human/queue actually reached

Then use the same controlled scenario set before and after any Retell-side or agent-side change.

The key distinction for me would be transfer requested vs. transfer completed. If the caller hears “connecting you now” but the downstream connection never occurs, I’d classify that as a hard business-outcome failure regardless of what the conversational trace looked like.

With a 0.8% measured baseline across 20k+ calls, you also have enough volume to tell whether a mitigation materially changed the failure rate rather than relying on a handful of successful test calls.

If you test another mitigation, I’d be interested in independently running a small post-fix regression set against the critical transfer paths and documenting Expected vs Observed + evidence. I’m currently validating that outside-in verification approach specifically for AI workflows where the conversation can look successful while the business action fails.

Hi @mark1 @alberto.rentas ,

I have to be direct: this doesn’t move us forward. It restates our own findings and adds a generic AI slop measurement framework.

We cannot reproduce this on demand, and we won’t change our agents based on a failure we can’t reproduce. Every number here comes from live production calls — 20,227 calls Aug 28–Sep 2, 153 with no transferToLiveAgent at all (0.8%, unchanged). Real customers heard “connecting you now” and were never connected. Asking us to re-check each week and send more Call IDs isn’t a workable loop: we’ve given sixteen IDs across two months, grouped into two patterns with word-level timestamps.

The three questions from Sep 2 are still open:

  1. Did the Aug 28 fix touch this path specifically — transfer_call pending while the agent is speaking, user speech arriving before TTS ends? Production says no change.
  2. For the pattern B calls, can you confirm from your logs that the transfer_call was received from the LLM? The agent speaks the execution_message, which only exists as an argument of that call — so it was emitted and then dropped by your runtime. Yes/no settles it.
  3. Can transfer_call be made to execute immediately, or survive a barge-in, instead of waiting for TTS?

And the main ask: tell us how to reproduce this in a test. Our harness calls the LLM directly and replays the conversation around the transfer — the model emits transfer_call every time. The model isn’t the problem; we do not know what is.

If the traces aren’t retrievable, that’s an acceptable answer — we’d rather know. But four weeks after the first escalation we need this with an engineer who can look at the transfer execution path directly. Happy to join a call any time.

Best regards,
Pavel

Hey @pkostadinov

I’ve raised your issue again with the relevant team. As soon as I receive an update from them, I’ll let you know promptly. Thank you for your patience

Best Regards.

Hey @pkostadinov

The Product Team is currently reviewing this issue. We’ll keep you updated as soon as we receive any further information.

Best Regards.

Hey @pkostadinov

Our team has identified this as a bug and is currently working on a fix specifically for this issue.

We’ll provide an update once the fix is available.

Best Regards.

I ran into almost the exact same thing on one of my agents, also with a lookup tool followed by the transfer, and it was maddening because it only happened every so often.

What I figured out is that the problem was the agent saying the announcement as its own turn. Once the model has spoken “please hold while I transfer you,” it sometimes treats that as the action being done and never follows up with the actual tool call. So it thinks the transfer is in progress when it never happened.

What fixed it for me was taking the announcement out of the prompt completely. I told the agent not to say anything about transferring and to just call the transfer tool, and then I put the “please hold while I transfer you” message in the transfer tool’s own settings so Retell speaks it while executing. That way the words and the action can’t get separated anymore. I haven’t seen it happen since.

Hey @pkostadinov
Thanks for your patience. We went through the LLM request logs for the 16 calls you sent and for every flagged call from the last week. Here is what is happening. The calls you sent, June 5 to August 11 In none of these did the model call transferToLiveAgent and then have it dropped. The model replied with text only. That includes the pattern B calls: in call_015eda7db3fa22872faf2a4c8e3, “One moment. Connecting you now.” was ordinary model text, not the tool’s execution message. 13 of the 16 came after an agent swap. Because of an issue on our side, the history we sent to the model put the swap result (“TRANSFER SUCCESSFUL…”) after the getTransferPhone result, so the model thought the transfer had already happened. Your harness rebuilt the history in the correct order, which is why it could never reproduce this. That issue was fixed in late August. 2 of the 16 were a rarer case where the caller spoke while getTransferPhone was still running. That case did not occur in the last week. What your metric counts now.
Of the 152 calls flagged from September 27 to October 4: 117: the caller hung up, mostly during the transfer announcement. The transfer starts after the agent finishes speaking, so these never show a transferToLiveAgent call.
23: the caller said something new right after the number lookup (“No.”, “Goodbye.”) and the agent answered that.
12: getTransferPhone returned an HTTP error, “Unknown”, or no number. We found no call where the agent finished the announcement, the call continued, and the transfer silently failed.
Suggestions :

1: Leave out of your metric calls that end in a caller hang-up shortly after getTransferPhone returns, and calls where it returns no number.

2: Keep what the agent says before the transfer short, because the dial starts only after the agent has said it.
3: Check the getTransferPhone errors and “Unknown” values on your side.

Best Regards.