Skip to content
Godwill Barasa

M-Pesa · Reference

Daraja result codes and API errors

Safaricom does not publish a consolidated list. This is the one I keep, from 10 result codes and 3 API errors I have hit running M-Pesa on four platforms.

Two families, routinely confused

errorCode — dotted strings like 400.002.02, returned in the HTTP response when your request is rejected before it reaches the customer. Your request was wrong.

ResultCode — integers like 1032, delivered to your callback after the customer’s handset has been involved. Your request was fine; something happened at the other end.

A 200 OK on the push request means Safaricom accepted it, not that the customer paid. Treating acceptance as payment is the defect behind most double-charges — see idempotency and reconciliation.

13 of 13
  1. 0ResultCode · callbackRetry will not help#

    Success

    What causes it
    The customer entered their PIN and the debit went through.
    What to do
    The only code that means money moved. Reconcile against the MpesaReceiptNumber in the callback, not against your own request id — and make the handler idempotent, because this callback can arrive more than once.
    Attested
    Universal across Safaricom documentation and every community source.
  2. 1ResultCode · callbackRetry can help#

    Insufficient balance

    What causes it
    The customer has less in their M-Pesa account than the amount requested.
    What to do
    Not a failure of your integration. Tell the customer the balance was short and offer to retry at a lower amount if your product allows part payment.
    Attested
    Settled across community sources.
  3. 17ResultCode · callbackRetry can help#

    Internal failure

    What causes it
    A fault inside M-Pesa, not in your request.
    What to do
    Retry once after a short delay. If it repeats across different customers in a short window, it is an M-Pesa incident and no amount of retrying will fix it — fail gracefully and queue.
    Attested
    Community sources; not individually documented by Safaricom.
  4. 26ResultCode · callbackRetry can help#

    System busy

    What causes it
    M-Pesa is under load and shed your request.
    What to do
    Back off and retry. This one clusters: if you see it at all, you will see a lot of it at once. Exponential backoff with jitter, not a fixed retry.
    Attested
    Community sources.
  5. 1001ResultCode · callbackRetry can help#

    Unable to lock subscriber, transaction in progress

    What causes it
    The customer already has an STK prompt open — often one YOU sent seconds earlier because they double-clicked your pay button.
    What to do
    Do not retry immediately; you will get the same code. Wait roughly 30 seconds for the existing prompt to expire. Better: stop the second request leaving your server at all by locking on the customer phone number for the lifetime of a prompt.
    Attested
    Settled; also surfaces as errorCode 500.001.1001 on the synchronous path.
  6. 1019ResultCode · callbackRetry can help#

    Transaction expired

    What causes it
    The prompt reached the handset and the customer left it until it timed out.
    What to do
    Distinct from 1037: the prompt ARRIVED here. Safe to offer an immediate retry, because the customer is likely still at their phone.
    Attested
    Community sources.
  7. 1025ResultCode · callbackRetry can help#

    An error occurred while sending a push request

    What causes it
    The push failed on its way out, frequently alongside a malformed field.
    What to do
    Check the amount and the account reference before retrying. An amount with decimals is a common trigger — M-Pesa works in whole shillings.
    Attested
    Community sources.
  8. 1032ResultCode · callbackRetry can help#

    Request cancelled by user

    What causes it
    The customer saw the prompt and pressed cancel.
    What to do
    The most common non-success code in production, and it is not an error. Never show it as a failure — show a retry. Treating 1032 as an error is why so many Kenyan checkouts read as broken when the customer simply changed their mind.
    Attested
    Universal across community sources.
  9. 1037ResultCode · callbackRetry can help#

    DS timeout, user cannot be reached

    What causes it
    The prompt never got to the handset: phone off, out of coverage, or on a call long enough to miss it.
    What to do
    Distinct from 1019: nothing reached the customer. Retrying straight away usually fails the same way. Ask them to confirm the line is on before you try again.
    Attested
    Settled across community sources.
  10. 2001ResultCode · callbackRetry can help#

    Wrong PIN

    What causes it
    The customer entered an incorrect M-Pesa PIN.
    What to do
    Offer a retry. Note the ambiguity below before you write a message around this code.
    Attested
    DISPUTED. Attested as "wrong PIN" in the STK Push callback context, but some sources list 2001 as "invalid initiator information". The initiator case belongs to the B2C and reversal APIs, which authenticate differently. Log the full callback rather than branching on this code alone.
  11. 400.002.02errorCode · responseRetry will not help#

    Bad request — invalid phone number

    What causes it
    The MSISDN is not in the format Daraja accepts. It wants 2547XXXXXXXX or 2541XXXXXXXX: no plus, no leading zero, no spaces.
    What to do
    Normalise on the way in, not at the call site. Kenyan users type 07…, +2547…, and 7… interchangeably, and all three have to become the same string before they reach Daraja.
    Attested
    Community sources; consistent wording across them.
  12. 404.001.03errorCode · responseRetry will not help#

    Invalid access token, or URL not found

    What causes it
    Almost always an environment mismatch: sandbox credentials against the production host, or the reverse.
    What to do
    Check which base URL the token was minted against. The message says "not found", which sends people hunting for a typo in the path when the credentials are the actual problem.
    Attested
    Community sources.
  13. 500.001.1001errorCode · responseRetry can help#

    Unable to lock subscriber, a transaction is already in process

    What causes it
    The synchronous twin of ResultCode 1001. The customer has an open prompt already.
    What to do
    Wait about 30 seconds. Handling this on the synchronous path and 1001 on the callback path separately is a common source of duplicated retry logic — treat them as one condition.
    Attested
    Community sources; pairs with ResultCode 1001.

How this list is maintained

Every row says where its meaning is attested. Where community sources disagree — 2001 is the live example — the row says so rather than picking a winner, because a reference that quietly guesses is worse than no reference. Last reviewed 2026-10-21.