Skip to main content

Handling USSD Errors

USSD has three recurring failure modes: invalid input, session timeouts, and backend errors. Handling each cleanly keeps users from abandoning mid-flow. This guide shows the patterns.

⚠️ The Three Failure Modes​

FailureCauseResponse
Invalid inputWrong key / out-of-rangeRe-prompt with valid options
TimeoutSession expired by carrierGraceful END, suggest re-dial
Backend errorYour service or upstream failedEND with a clear apology

🔁 Invalid Input​

Re-prompt instead of dead-ending. Cap retries so users aren't trapped.

def handle_menu(text, attempt):
if text in ('1', '2', '3'):
return route(text)
if attempt >= 2:
return "END Too many invalid entries. Please dial again."
return "CON Invalid choice.\n1. Balance\n2. Airtime\n3. Send money"
function validate(input, valid) {
if (valid.includes(input)) return { ok: true };
return { ok: false, msg: `CON Invalid entry. Choose: ${valid.join(', ')}` };
}

⏱️ Timeouts​

You can't extend a carrier session, but you can design around it and recover on re-dial.

  • Keep each step completable in a few seconds.
  • Persist progress keyed by phone so a re-dial can offer "Resume".
  • Don't leave half-finished transactions in a pending state — roll them back.
def on_resume(phone):
pending = store.get(f"resume:{phone}")
if pending:
return f"CON Resume {pending['action']}?\n1. Yes\n2. Start over"
return root()

🛠️ Backend Errors​

Never expose stack traces or hang. Fail fast with a friendly message and a fallback.

async function handle(req) {
try {
return await process(req);
} catch (err) {
log(err);
return 'END Sorry, something went wrong. Please try again shortly.';
}
}

For money operations, confirm the final state out-of-band by SMS so the user has a record even if the session dropped.

💡 Best Practices​

  • Re-prompt on invalid input, but cap retries (2) then END.
  • Never expose raw errors to the user.
  • Respond within the gateway timeout — slow replies look like errors.
  • Send an SMS receipt for any money movement, in case the session drops.
  • Roll back incomplete transactions rather than leaving them pending.
  • Log session IDs with errors for debugging.

Last Updated: May 2026