Troubleshooting
Common SMPP bind errors, connection failures, and how to fix them.
Connection refused / timeout
Symptom: Connection refused or Connection timed out when connecting to smpp.esmsafrica.io:2775.
Causes and fixes:
-
Firewall blocking outbound port 2775 - Ensure your network allows outbound TCP on port 2775. Check both host firewall (
iptables,ufw) and network-level rules. -
Wrong port - The only supported port is
2775. There is no port 8775, 5775, or plaintext 2775. -
DNS resolution failure - Verify
smpp.esmsafrica.ioresolves:nslookup smpp.esmsafrica.io.
TLS handshake failure
Symptom: Connection establishes but drops immediately, or you see SSL handshake failed / protocol version errors.
Causes and fixes:
-
Client does not support TLS 1.3 - TLS 1.3 is required. Upgrade your TLS library. Python:
ssl.PROTOCOL_TLS_CLIENTwith Python 3.7+ and OpenSSL 1.1.1+. Node.js: 12+ supports TLS 1.3 natively. -
smpplib not passing TLS context - Pass a
ssl.SSLContexttoclient.connect(ssl=ctx), notssl=True:ctx = ssl.create_default_context() client.connect(ssl=ctx) # correct -
Corporate proxy stripping TLS - Some proxies intercept TCP. Connect directly or configure a TLS passthrough.
ESME_RBINDFAIL (0x0000000E) - Bind rejected
Symptom: bind_*_resp returns command_status=0x0000000E.
Causes and fixes:
-
Wrong system_id or password - Double-check your credentials. Credentials are case-sensitive.
-
system_type mismatch - Leave
system_typeas an empty string""unless you were specifically told otherwise. -
IP not whitelisted - If your account has IP whitelisting enabled, ensure the source IP of your connection is on the allowlist. Contact noc@esmsafrica.io to add an IP.
-
Too many concurrent binds - If you have reached your concurrency limit, new binds will be rejected. Unbind idle sessions or request a higher limit.
ESME_RINVSRCADR (0x0000000A) - Invalid source address
Symptom: submit_sm_resp returns command_status=0x0000000A.
Cause: The source_addr (sender ID) you used has not been approved on your account.
Fix: Use an approved sender ID. Check your account portal for the list of approved sender IDs, or request a new one. In the meantime you can use your account's default sender ID.
Also verify source_addr_ton matches the sender type:
- Alphanumeric text ID →
source_addr_ton=5,source_addr_npi=0 - E.164 number →
source_addr_ton=1,source_addr_npi=1
ESME_RTHROTTLED (0x00000058) - Rate limit exceeded
Symptom: submit_sm_resp returns command_status=0x00000058.
Cause: You are submitting faster than your configured throughput window.
Fix:
- Add a rate limiter in your client code to stay within the configured TPS.
- Open multiple binds and distribute submissions across them.
- Contact noc@esmsafrica.io to increase your throughput limit.
import time
class RateLimiter:
def __init__(self, tps: int):
self.min_interval = 1.0 / tps
self.last = 0.0
def wait(self):
elapsed = time.monotonic() - self.last
if elapsed < self.min_interval:
time.sleep(self.min_interval - elapsed)
self.last = time.monotonic()
limiter = RateLimiter(tps=80) # stay under 100 TPS limit
for msg in messages:
limiter.wait()
client.send_message(...)No DLR received
Symptom: Messages are delivered but no deliver_sm DLR arrives on your session.
Causes and fixes:
-
registered_deliverynot set - You must setregistered_delivery=1in everysubmit_smto request a DLR. -
Using a TX-only bind - DLRs can only arrive on a TRX (
bind_transceiver) or RX (bind_receiver) bind. A pure TX bind cannot receive PDUs. -
Not calling
listen()- In smpplib, you must callclient.listen()to enter the receive loop. Without it, inbound PDUs are silently dropped. -
Not acknowledging deliver_sm - If you don't respond with
deliver_sm_resp, the gateway stops sending DLRs on that bind after a short time. Always acknowledge everydeliver_sm. -
Short code / long code not configured for MO - For MO traffic, your number must be registered for inbound routing. Contact support.
Duplicate DLRs
Symptom: The same DLR is received multiple times.
Cause: Your application did not acknowledge deliver_sm within the timeout window, causing the gateway to resend.
Fix: Respond to every deliver_sm immediately with deliver_sm_resp. Store processed message_id values and deduplicate if necessary:
seen_ids = set()
def message_received(pdu):
msg = pdu.short_message
if isinstance(msg, bytes):
msg = msg.decode('latin-1')
dlr = parse_dlr(msg)
if dlr and dlr['id'] not in seen_ids:
seen_ids.add(dlr['id'])
process_dlr(dlr)Connection drops / lost bind
Symptom: Bind succeeds but the connection closes after a period of inactivity.
Fix: Send enquire_link every 30 seconds and handle enquire_link_resp. Most libraries support this via a timer or auto_enquire_link_period option.
import threading
def keepalive(client, interval=30):
while True:
time.sleep(interval)
try:
client.enquire_link()
except Exception:
break # connection lost - trigger reconnect
threading.Thread(target=keepalive, args=(client,), daemon=True).start()Implement reconnect logic with exponential backoff:
import time
def connect_with_retry(system_id, password, max_attempts=10):
delay = 2
for attempt in range(max_attempts):
try:
ctx = ssl.create_default_context()
client = smpplib.client.Client('smpp.esmsafrica.io', 2775, timeout=30)
client.connect(ssl=ctx)
client.bind_transceiver(system_id=system_id, password=password)
return client
except Exception as e:
print(f'Attempt {attempt + 1} failed: {e}. Retrying in {delay}s...')
time.sleep(delay)
delay = min(delay * 2, 60)
raise RuntimeError('Could not reconnect after max attempts')Getting help
If you cannot resolve an issue:
- Collect the full error, bind credentials (system_id only, never password), and your client library version.
- Email noc@esmsafrica.io or use the chat in the portal.
NOC engineers are available 24/7 for production issues.