osmTalk Docs
Calls

Failure Reasons

What each call failure reason means, who's likely at fault, and what to try next.

When a call ends without producing a real conversation, osmTalk marks it status: "failed" and stamps a machine-readable failureReason so you can act on it. This page explains every reason in plain English — the same copy appears in the dashboard, the SDK helper describeFailureReason(), and the example projects, so you'll see consistent wording everywhere.

Why this exists

A single "failed" label tells you something went wrong but nothing about why, whose fault it is, or whether to retry. The failure-reason system answers all three at once:

  • Why — a short cause description ("Bot connected but couldn't speak")
  • Whose faultlikelyBlame is platform, caller, carrier, or environment
  • What nextwhatToTry is a concrete action you can take
  • Retry?retryable: true | false

In the dashboard, every failed call's detail page shows a red banner with this information at the top.

Where you see failure reasons

SurfaceWhere
DashboardCall detail page — banner at the top of every failed call
REST APIGET /api/calls/{id} returns failureReason on the call object
TypeScript SDKclient.calls.get(id)call.failureReason; helper describeFailureReason(reason)
Webhookscall.failed event includes reason at top level and call.failureReason
Campaign retriesThe retry policy auto-retries calls where failureReason is retryable

Reading the dashboard banner

Each banner has three sections:

  1. Title + blame badge — at-a-glance "who's the problem"
  2. Cause — one sentence explaining what actually happened
  3. What to try — the next action

For testers worried that "failed" means the platform is broken — read the blame badge first. If it says "Caller behaviour" or "Environment / network", the platform did its job; the test conditions were the limiting factor.

The full reason list

no_audio_output — Bot connected but couldn't speak

Likely blame: platform · Retryable:

Cause. The bot joined the call but its text-to-speech connection failed — usually a provider rate-limit or transient outage during a concurrent-call burst.

What to try. Retry the call. If it keeps happening on more than 5% of concurrent calls, lower the campaign maxConcurrent or switch TTS provider (Voice settings → TTS).


no_audio_either_direction — Neither side produced audio

Likely blame: environment · Retryable:

Cause. The call connected at the network layer but no audio ever flowed in either direction — the caller's mic was off, the line was dead, or both bot and caller were unable to negotiate audio.

What to try. Ask the caller to check their mic permissions / phone speaker. If you're testing, make sure you're in a quiet environment and not muted. Then retry.


idle_timeout — No activity for too long

Likely blame: caller · Retryable:

Cause. Neither side spoke for the configured idle window (default 5 minutes). The call was ended automatically to free the slot.

What to try. If this is a test, speak within 30 seconds of the bot's welcome message. For real calls, this is usually a caller who walked away — retry per your campaign policy.


provider_circuit_open — Provider error storm

Likely blame: platform · Retryable:

Cause. The bot saw 3 or more provider errors (TTS/STT/LLM) within 60 seconds and ended the call gracefully instead of letting the caller listen to silence.

What to try. Check provider health in the dashboard (top of the agent page). If a provider shows degraded, swap to a backup. Then retry. The circuit breaker is doing its job — it's the right behaviour when a provider is misbehaving.


sip_no_answer — The number didn't pick up

Likely blame: carrier · Retryable:

Cause. The carrier reported the destination rang out with no answer (or was busy / unreachable).

What to try. Campaign retry policies handle this automatically. For test numbers, verify the line is live and able to receive calls.


sip_rejected — Carrier rejected the call

Likely blame: carrier · Retryable:

Cause. The phone carrier refused the call — usually because the destination number is invalid, blacklisted, or your trunk lacks geo permissions for that country.

What to try. Verify the destination number is in E.164 format and reachable from your origin trunk. Check Plivo/Twilio geo permissions if calling internationally.


bot_startup_failed — Bot couldn't start in time

Likely blame: platform · Retryable:

Cause. The bot process didn't confirm it was ready within the startup window (15 seconds by default). Usually a transient provider WebSocket handshake issue on cold-start.

What to try. Retry — first-call-after-deploy is the most common case and is harmless on retry. If it persists, check bot logs for provider auth errors.


caller_hung_up_silently — Caller answered and immediately hung up

Likely blame: caller · Retryable:

Cause. Caller picked up but disconnected within the first second or two — before the bot finished its welcome message. Often a real person ignoring an unknown number.

What to try. Retry per your campaign policy. If you see this on a high percentage of leads, consider shortening your welcome message — long welcomes correlate with quick hang-ups.


stale_sweep — Call was orphaned

Likely blame: platform · Retryable:

Cause. The bot process died mid-call without reporting back. The background sweeper found the call orphaned and marked it failed.

What to try. Retry. If you see this on more than 1% of calls, check bot container restart logs and platform alerts.


unknown — Unclassified failure

Likely blame: unknown · Retryable: ❌ (by default — don't retry blindly)

Cause. The call ended in a way the platform couldn't classify.

What to try. Open the call detail's events tab for full logs. Contact support@osmapi.com if you see this regularly.


What to tell your testers

A common reaction to seeing a "failed" badge is: "the platform is broken!" It usually isn't — but the tester is in a bad position to know that. Here's the framing that helps:

Failed doesn't mean broken. Failed means the call ended without a real conversation — and there's a specific reason for it.

Open the call. There's a red banner that tells you:

  1. What happened (one sentence)
  2. Whose problem it is — platform, caller, environment, or carrier
  3. What to try next

If the banner says "Environment / network" or "Caller behaviour", retry from a quieter location with the test phone unmuted. If it says "Platform-side", retry once — if it happens again, that's worth reporting.

Paste this into your tester onboarding doc.

How retryability is computed

isRetryableFailure(reason) (exported by the TypeScript SDK) returns true for:

no_audio_output, no_audio_either_direction, idle_timeout,
provider_circuit_open, sip_no_answer, bot_startup_failed, stale_sweep

…and false for the rest. Campaign workers use this to decide whether to schedule a follow-up attempt without bothering the operator. If you're building a custom outbound flow, do the same:

import { isRetryableFailure } from "@osmapi/osmtalk-sdk";

if (call.status === "failed" && isRetryableFailure(call.failureReason)) {
  await retry(call);
} else if (call.status === "failed") {
  // Don't retry permanent failures — surface to a human.
  await logForHumanReview(call);
}

Programmatic guidance (SDK helper)

If you're building a UI that displays failure reasons (CRM, dashboard, ops console), use describeFailureReason() from the SDK so your copy stays in sync with osmTalk's official wording:

import { describeFailureReason } from "@osmapi/osmtalk-sdk";

const g = describeFailureReason(call.failureReason);
console.log(g.title);        // "Bot connected but couldn't speak"
console.log(g.cause);        // 1-sentence cause
console.log(g.likelyBlame);  // "platform" | "caller" | ...
console.log(g.whatToTry);    // actionable next step
console.log(g.retryable);    // boolean

The exact same object shape powers the dashboard banner and the 02-bulk-campaign example's terminal output.

Webhook examples

When a call fails, osmTalk fires a call.failed webhook with both top-level and per-call reason fields:

{
  "event": "call.failed",
  "timestamp": "2026-05-13T08:42:17.221Z",
  "reason": "no_audio_either_direction",
  "call": {
    "id": "call_xxx",
    "agentId": "agent_xxx",
    "channel": "phone",
    "startedAt": "2026-05-13T08:42:12.000Z",
    "endedAt": "2026-05-13T08:42:17.221Z",
    "durationSeconds": 5,
    "failureReason": "no_audio_either_direction"
  }
}

The 03-webhook-receiver example shows how to branch on reason and surface the same dashboard-style copy in your CRM logs.

What if I disagree with the classification?

The reasons are derived from bot/API telemetry — they're our best guess, not ground truth. If you see a call you believe was a real conversation marked as no_audio_either_direction (or vice versa), open the call detail page, click the events tab, and email a screenshot to support@osmapi.com. The classification heuristics are tuned regularly.