command_status is the 4-byte header field that reports the outcome of an operation. In a request it is 0 (ESME_ROK); in a response it carries an ESME_R* error code. 0x00000000 is success; non-zero values indicate specific faults such as invalid password, throttling, or a full message queue.
How command_status works
Every response PDU echoes the request's sequence_number and sets command_status to the result. ESME_ROK (0) means success. Any other value is an error whose numeric code maps to a named ESME_R* constant in the spec. Codes 0x00000100–0x000003FF are reserved for SMPP extensions and 0x00000400–0x000004FF for SMSC-vendor-specific errors, so always handle unknown codes gracefully.
The codes you will actually see
A small set of codes covers most real-world handling. Group them by response: retry-after-backoff (throttling, queue full, system error), fix-the-request (invalid address, TON/NPI, data_coding, message length), and fatal-for-the-session (invalid password or system id, bind failure).
| Value | Name | Meaning | Typical reaction |
|---|---|---|---|
| 0x00000000 | ESME_ROK | No error | Success |
| 0x00000001 | ESME_RINVMSGLEN | Invalid message length | Fix payload / segment |
| 0x00000005 | ESME_RALYBND | Already bound | Do not re-bind; reuse session |
| 0x0000000A | ESME_RINVSRCADR | Invalid source address | Fix sender / TON / NPI |
| 0x0000000B | ESME_RINVDSTADR | Invalid destination address | Fix recipient MSISDN |
| 0x0000000D | ESME_RBINDFAIL | Bind failed | Back off, check credentials |
| 0x0000000E | ESME_RINVPASWD | Invalid password | Fatal — fix credentials |
| 0x0000000F | ESME_RINVSYSID | Invalid system ID | Fatal — fix credentials |
| 0x00000014 | ESME_RMSGQFUL | Message queue full | Back off and retry |
| 0x00000043 | ESME_RINVESMCLASS | Invalid esm_class | Fix esm_class bits |
| 0x00000058 | ESME_RTHROTTLED | Throttling — rate exceeded | Slow down, back off with jitter |
| 0x00000062 | ESME_RINVEXPIRY | Invalid validity_period | Fix validity format |
| 0x000000FE | ESME_RDELIVERYFAILURE | Delivery failure (data_sm) | Inspect / retry per policy |
| 0x000000FF | ESME_RUNKNOWNERR | Unknown error | Log, alert, retry cautiously |
Reserved and vendor ranges
Not every code you receive is in the core spec. The spec reserves 0x00000100–0x000003FF for future SMPP use and 0x00000400–0x000004FF for SMSC-specific codes, so your provider may return a value only their documentation explains. Map provider-specific codes to a normalised internal taxonomy (invalid number, blocked, throttled, temporary failure) so cross-route quality stays comparable.
| Range | Reserved for |
|---|---|
| 0x00000100 – 0x000003FF | SMPP protocol extensions |
| 0x00000400 – 0x000004FF | SMSC vendor-specific errors |
| 0x00000500 – 0xFFFFFFFF | Reserved |
Frequently asked
What does ESME_RTHROTTLED mean and how should I handle it?
ESME_RTHROTTLED (0x00000058) means you exceeded your allowed message rate. Stop increasing load, pause submits, and back off with jitter before retrying. Persisting at the same rate risks the provider dropping the bind.
Is ESME_RINVPASWD recoverable?
No, not at runtime. ESME_RINVPASWD (0x0000000E) indicates wrong credentials. Do not hot-loop the bind; fix the system_id/password configuration and reconnect. Repeated failed binds can trigger IP blocking.
What is the difference between ESME_RMSGQFUL and ESME_RTHROTTLED?
ESME_RMSGQFUL (0x00000014) means the SMSC's queue is temporarily full; ESME_RTHROTTLED (0x00000058) means you exceeded your rate limit. Both call for backoff, but throttling is about your TPS while queue-full is about the SMSC's capacity.
I received an error code that is not in the spec — why?
SMPP reserves 0x400–0x4FF for SMSC-vendor-specific codes, so your provider may return a value documented only by them. Consult the provider's error table and map it to your internal taxonomy.
Source specifications
The published standards this page is grounded in. Opens in a new tab.