Skip to content
Start free evaluation

Status codes and errors

The PDComm SECS/GEM SDK reports failures in two ways:

  • Call results report problems that are known when you make the call. C functions return a status code, the C++ wrappers throw pdcomm::Error, and the .NET binding throws PdcommException.
  • Asynchronous results report problems that appear later, after a request was sent. They arrive as HSMS events and GEM results. For example, a T3 reply timeout arrives as an event after the request call itself succeeded.

A successful request call only means that the SDK queued the request. It doesn’t mean the peer replied. Always confirm the reply or the failure through events.

C functions return PdcommStatus (int32_t). The .NET binding uses the same values in PdcommStatus.

Value C name .NET name Meaning What to do
0 PDCOMM_OK Ok The call succeeded. Nothing.
1 PDCOMM_INVALID_ARGUMENT InvalidArgument An argument is invalid: a NULL pointer, a wrong struct_size or version, a non-numeric address, a zero timer, or an odd function number in a reply. Zero-initialize option structures, then set struct_size and version. Write addresses as numbers, for example 127.0.0.1:5000.
2 PDCOMM_LIMIT_EXCEEDED LimitExceeded A limit set by your application was exceeded: message size, item count, nesting depth, or number of IDs. Limits are your policy, not standard values. Raise the limit in PdcommSecsLimits or the endpoint options if you need larger data.
3 PDCOMM_BUFFER_TOO_SMALL BufferTooSmall The output buffer is too small. required holds the size you need, and the buffer is unchanged. Allocate required bytes and call again.
4 PDCOMM_ALLOCATION_FAILED AllocationFailed A memory allocation failed. If it happened while you took an event, the SDK keeps that event. Free memory and repeat the call.
5 PDCOMM_INTERNAL_ERROR InternalError The SDK caught an internal failure, or the clock went backwards. Send the reproduction steps and the diagnostics log to support.
6 PDCOMM_INVALID_DATA InvalidData The SECS-II encoding or the data format is invalid. In .NET, ErrorOffset gives the byte position of the problem.
7 PDCOMM_TRUNCATED Truncated The SECS-II input ends too early. Make sure you pass all received bytes.
8 PDCOMM_OUT_OF_RANGE OutOfRange The ID doesn’t exist or the index is out of range. Check the registered IDs and the index range.
9 PDCOMM_BACKPRESSURE Backpressure The endpoint can’t take more work right now: 32 transactions are open, the same control request is already pending, or an event queue is full. Keep draining events, and send again after replies or failures arrive. Your application decides how often and how long to retry.
10 PDCOMM_INVALID_STATE InvalidState The call isn’t allowed in the current state, for example a request while not Selected, a status request before communication is established, or a setup call after start. Check the state and fix the order of calls.
11 PDCOMM_STALE_TOKEN StaleToken The token or event belongs to another endpoint or an earlier generation, or GEM already processed the event. After a reconnect, start again with new requests. Pass each event to GEM only once.
12 PDCOMM_IO_ERROR IoError A socket bind, accept, local address query, or write timed out or failed. pdcomm_license_host_id also returns it when the machine id cannot be read. When it happens while you create a passive endpoint, the port is usually in use. See Port already in use. When the host id query returns it, see Host id.
13 PDCOMM_POISONED Poisoned The endpoint caught an unexpected failure during a call. Every later call on that endpoint returns this value. Destroy the endpoint and create a new one. Send the reproduction steps and the log to support.
14 PDCOMM_PROGRAM_UNCONFIRMED ProgramUnconfirmed A process program was installed, but durable storage isn’t confirmed. Check the storage result and confirm again according to your policy.
15 PDCOMM_SPOOL_UNCONFIRMED SpoolUnconfirmed A spool change isn’t confirmed as durably stored. Keep the queue and the storage result, and retry according to your policy.
16 PDCOMM_LICENSE_REQUIRED LicenseRequired There’s no license for new communication. Creating and starting endpoints (pdcomm_tcp_create, pdcomm_tcp_start, pdcomm_secs1_create, pdcomm_secs1_start) needs the secs-gem module, and creating or replacing a GEM300 capability catalog needs gem300. The license is missing or expired, or doesn’t list the host id of this computer. Nothing was created or started. Check the module’s state with pdcomm_license_status and follow Install and check a license. Endpoints that are already running keep working.

The SECS/GEM SDK has no function that returns a last-error message. Use the status code, the codes in events, and the diagnostics log to find the cause. For PDCOMM_LICENSE_REQUIRED, the cause is in modules of the status JSON that pdcomm_license_status returns. With the diagnostics log on, every refusal also writes a license_required record.

The C++ wrappers throw Error, which derives from std::runtime_error. Its what() text is PDComm native operation failed for every status except PDCOMM_LICENSE_REQUIRED, whose text starts with PDComm license required (status 16), so it doesn’t tell you the cause. Catch const pdcomm::Error & and log the value of Error::status. The C++ tutorial peer does this when an F0 reply fails: it prints the status after REPLY ERROR status=.

The tutorial peer prints only what() for command errors. If you type values before communication is established, all you see is this:

COMMAND ERROR PDComm native operation failed

In the same situation, the C# peer also prints the status name (InvalidState). The logging wrapper DiagnosticLogging throws std::runtime_error with Cannot configure PDComm logging, Cannot query PDComm logging, or Cannot flush PDComm logging. The license wrapper pdcomm::License (pdcomm_license.hpp) also throws std::runtime_error, with Cannot install the PDComm license, Cannot query the PDComm license status, or Cannot read the PDComm host id.

Exception When it happens What to do
PdcommException A native call failed. Status holds the status code, and ErrorOffset holds the byte position of a decoding failure. The message looks like PDComm native operation failed with status InvalidState. When Status is LicenseRequired, the message ends with the module states, as in License state: secs-gem missing, gem300 missing. Follow the status code table above. For licenses, see the details with PDComm.License.Status().
System.DllNotFoundException PdcommRuntime.Initialize can’t load the native library from the path you passed. Check the absolute path, the file name, and the process architecture (x64 or arm64).
ArgumentException You passed a relative path to PdcommRuntime.Initialize. Pass an absolute path.
NotSupportedException The native library’s ABI isn’t between 0.17 and 0.30, or a required function is missing. Use PDComm.dll and the native library from the same SDK bundle.
InvalidOperationException The runtime was already initialized with a different library, or a DiagnosticLogging call failed. Use one native library per process. Check the logging JSON and the file path.
TimeoutException A worker couldn’t drain and close before its shutdown deadline. Make sure you keep reading events, and give the shutdown option enough time.

When a worker fails to process an event, it doesn’t throw. It records the status in GemEvent.ProcessingStatus. When the worker’s command queue is full, you get PdcommException with Backpressure.

When you pass a TCP event to GEM (pdcomm_gem_handle_event), you get a result with one of these kinds. The .NET type is GemResultKind.

Value C name .NET name Meaning
0 PDCOMM_GEM_IGNORED Ignored The event has nothing to do with GEM, such as a state change, protocol error, or transport error.
1 PDCOMM_GEM_PRIMARY Primary The peer sent a request (a primary message). GEM may have replied automatically.
2 PDCOMM_GEM_RESPONSE Response A valid reply to one of your requests arrived.
3 PDCOMM_GEM_FAILED Failed Your request ended without a reply. code holds the reason.
4 PDCOMM_GEM_INVALID_RESPONSE InvalidResponse A reply arrived but failed validation. code holds a GEM error.
5 PDCOMM_GEM_UNHANDLED Unhandled GEM didn’t handle a received request and sent no reply. code holds a GEM error.

For Failed, code is an HSMS failure reason (1–8 in the table below), or 12 (PDCOMM_GEM_ERROR_ABORTED) when the peer aborted the transaction with F0. For InvalidResponse and Unhandled, code is a GEM error (GemError):

Value GEM error Meaning
1 Disconnected No session is selected.
2 State The current communication or control state doesn’t allow the message.
3 Unsupported The stream and function aren’t supported for this role or configuration. Received S9Fx messages also get this code.
4 Wait The W-bit isn’t what the message requires.
5 MissingBody The message needs a body but has none.
6 UnexpectedBody The message must not have a body but has one.
7 Body The body structure is invalid.
8 Ack The acknowledge code value is invalid.
9 Response The reply isn’t the one the request expects.
10 Codec SECS-II decoding failed.
11 Correlation The reply doesn’t match the request.
12 Aborted The peer aborted the transaction with F0.

The tutorial peers print each GEM result as RESULT kind=<kind> request=<request> outcome=<outcome>. Results for received requests (kind=1) show request=0. These lines come from real runs:

Output Printed by Meaning
RESULT kind=2 request=1 outcome=2 Host S1F13 established communication.
RESULT kind=2 request=3 outcome=5 Host S1F3 status values arrived.
RESULT kind=1 request=0 outcome=13 Equipment S1F3 arrived and GEM replied automatically.
RESULT kind=3 request=3 outcome=0 Host S1F3 ended without a reply, for example after T3.

Request kinds 1–7 are listed below. Values 8 and higher cover reports, alarms, remote commands, process programs, and spooling; see GemRequestKind for the full list.

Value Request Message
1 Establish S1F13
2 AreYouOnline S1F1
3 StatusValues S1F3
4 StatusNames S1F11
5 Offline S1F15
6 Online S1F17
7 OperatorOnline Operator switch to online on the equipment

Outcomes 0–13 are listed below. For values 14 and higher, see GemOutcome.

Value Outcome Value Outcome
0 None 7 OfflineAccepted
1 Replied 8 OnlineAccepted
2 CommunicationEstablished 9 AlreadyOnline
3 CommunicationRejected 10 OnlineRejected
4 EquipmentIdentity 11 OfflineAck
5 ValuesReceived 12 OnlineAck
6 NamesReceived 13 StatusReplied

The tutorial peers don’t print code for a Failed result. To see the reason, look for the transaction_failed record in the diagnostics log.

Events you take from a TCP endpoint (pdcomm_tcp_next_event) have one of these kinds. The .NET type is HsmsEventKind.

Value Event Meaning
1 STATE_CHANGED The connection state changed. See from_state and to_state.
2 RECEIVED A message arrived. is_reply is 1 for a reply, and aborted is 1 for an F0 reply.
3 FAILED A request ended without a reply. code is a failure reason from the next table.
4 PROTOCOL_ERROR Received data broke the protocol. code is a protocol error from the table below.
5 TRANSPORT_ERROR A socket operation failed. code is the operation and detail is the I/O error kind.
6 W0_WRITTEN A W0 message was written completely. This doesn’t prove that the peer received it.

The .NET type is HsmsFailureReason.

Value Reason Meaning
1 Cancelled Your application cancelled the request or stopped the endpoint.
2 T3Timeout No reply to a data message arrived within T3. The connection stays up.
3 T6Timeout No reply to Select, Deselect, or Linktest arrived within T6. The connection drops next.
4 Disconnected The connection dropped while the request waited for a reply.
5 Deselected The peer ended communication with Deselect.
6 Rejected The peer rejected the message with Reject.req. value1 is the rejected type and value2 is the reason code.
7 SelectStatus Select.rsp carried a nonzero status. value1 is the status.
8 DeselectStatus Deselect.rsp carried a nonzero status. value1 is the status.

The .NET type is HsmsProtocolError.

Value Error Meaning
1 InvalidFrame The header or body of a received frame is malformed. detail holds the specific reason, 7 to 13 (PType, SType, session ID, header field, control message body, SECS-II body and so on). When the framing of the byte stream itself breaks, such as an invalid length field, the stream can’t be resynchronized, so the endpoint closes the connection with a transport error (Read, InvalidData) instead of this event.
2 FrameLimit Reserved; this version doesn’t produce it. A received frame larger than max_frame_bytes closes the connection with a transport error (Read, InvalidData). An outgoing body that doesn’t fit in a frame makes the request call return PDCOMM_LIMIT_EXCEEDED.
3 WrongSession A data message carried another session ID and was dropped. value1 is the expected ID and value2 is the received ID.
4 DuplicatePrimary A request with the same system bytes is still open.
5 IncomingLimit A W1 request arrived while all 32 incoming slots held W1 requests that still wait for your reply. The SDK answers the new request with F0 automatically.
6 UnexpectedReply A reply arrived with no open transaction, typically a late reply after T3.
7 UnexpectedControl Reserved; this version doesn’t produce it. An unmatched control reply is answered with Reject.req (reason 3) instead of an event.
8 InvalidReject A Reject.req arrived with no matching transaction.
9 InvalidReply Reserved; this version doesn’t produce it.

code is the operation that failed (HsmsIoOperation): 1 Bind, 2 LocalAddress, 3 Connect, 4 Configure, 5 Accept, 6 Read, 7 Write.

detail is the I/O error kind (HsmsIoErrorKind). These are the values you see most often:

Value Kind Typical situation
3 Refused Nothing listens on the port the active side connects to.
4 Reset The peer reset the connection.
7 AddressInUse Another program already uses the address and port.
8 AddressUnavailable The IP address doesn’t belong to this computer.
11 TimedOut A connect or write didn’t finish in time.
13 UnexpectedEof The peer closed the connection.
15 InvalidData The connection closed in the middle of a frame, or the endpoint closed it because a received frame had an invalid length field or exceeded max_frame_bytes.

The full list is 0 Other, 1 NotFound, 2 Permission, 3 Refused, 4 Reset, 5 Aborted, 6 NotConnected, 7 AddressInUse, 8 AddressUnavailable, 9 BrokenPipe, 10 WouldBlock, 11 TimedOut, 12 Interrupted, 13 UnexpectedEof, 14 InvalidInput, 15 InvalidData, and 16 WriteZero. The SDK handles WouldBlock internally, so your application never sees it.

The PDComm SDK never sends S9Fx error messages on its own; the PDComm simulator behaves differently, as How the simulator answers at the end of this section explains. When the peer sends an S9Fx message, your application gets an Unhandled result with code 3 (Unsupported).

When a received W1 request is Unhandled, the SDK sends no reply, so your application must answer it:

If you don’t reply, two things go wrong:

  1. The peer waits until T3 expires and then fails the request.
  2. The unanswered W1 request keeps holding an incoming slot. Once 32 slots are taken, the SDK answers every further W1 request with an automatic F0 and reports an IncomingLimit protocol error. From then on, even normal requests such as S1F3 fail.

The tutorial peers abort every W1 request they don’t handle with F0. When the C# host sends alarms (S5F5) to the C++ tutorial equipment, which has no alarm catalog, the equipment prints this:

RESULT kind=5 request=0 outcome=0
SECS-II 01 00
ABORT S5F0 for S5F5

The host prints this:

RESULT kind=3 request=13 outcome=0 status=None

The host’s result is Failed with code 12 (Aborted), and it arrives right away instead of after T3.

The PDComm simulator answers unhandled primary messages itself, as SEMI E5 Stream 9 describes. While an Equipment-role session is communicating, it answers as follows:

Received primary Simulator’s answer
A stream the equipment doesn’t recognize S9F3 (Unrecognized Stream Type)
An unrecognized function in a known stream S9F5 (Unrecognized Function Type)
A message with a malformed body S9F7 (Illegal Data)
A known message it can’t serve right now, such as S5F5 without an alarm catalog SxF0 only

Each S9Fx carries the 10-byte header of the received message (MHEAD) and has no W-bit. When the received primary has its W-bit set, the simulator also closes the transaction with function 0 of the same stream (SxF0), so the peer doesn’t wait for T3 and no incoming slot stays taken. A Host-role session, or a session that isn’t communicating yet, sends no S9Fx and answers W-bit requests with SxF0 only.