← Articles
SECS/GEM/12 min read/ views

Your SECS/GEM Host Logs 'No Reply' and the Equipment Did Answer

A real HSMS capture from 127.0.0.1:5501: request and mismatched Select.rsp decoded byte for byte, what a host's SystemBytes-keyed pending reply table does with the frame, and why the timer that fires is T6 and not T3.

SECS/GEMMESSCADATroubleshootingChecklists

The host log says no reply, transaction timed out. The equipment vendor says their log shows the response was sent. Both are telling the truth. The two frames below were on a real socket, and the one that went out differs from the one that came back by a single byte at the end. That byte is why the host refuses to count the reply as its own.

Select.req and the Select.rsp that came back, byte for byte: length prefix, SessionID, bytes 2-3, PType and SType all identical, and only bytes 6-9 — the SystemBytes — differ, 00 00 00 42 out against 00 00 00 43 back TX Select.req 0000000A 000B0000 0001 00000042 RX Select.rsp 0000000A 000B0000 0002 00000043 Length = 10 SessionID bytes 2-3 PType / SType SystemBytes — bytes 6-9

Every hex string below came off a socket. EQ1 on the SECS/GEM simulator ran as a passive listener on 127.0.0.1:5501, driven from outside by a plain Python socket client over a fresh TCP connection per run, and only frames the equipment side logged as RX are shown. The times are measured at the client. Captured 2026-09-18 with one fault flag, wrongSystemBytes, and the raw export is committed at content/demos/hsms-systembytes-mismatch.json.

The host pairs replies on SystemBytes

The SEMI E37 header is the 10 bytes that follow the 4-byte length prefix. E37 numbers those bytes from zero.

offsetfieldSelect.reqSelect.rsp
0-1SessionID00 0B (11)00 0B (11)
2zero on a control message / W-bit + Stream on a data message0000
3per-SType use on a control message / Function on a data message0000 = Select Status 0, accepted
4PType0000
5SType01 Select.req02 Select.rsp
6-9SystemByteswhatever the request chosethe request's value, unchanged

E37 requires the reply to carry back the request's SystemBytes unchanged, because that is the only thing pairing a transaction. So a host stack usually looks like this: sending a request inserts an entry into a pending reply table keyed on the SystemBytes, and starts a timer. An inbound frame is looked up in that table by header bytes 6-9.

A lookup that misses has no waiter to wake. The entry sits in the table until its timer expires, and the frame that arrived is delivered nowhere. Most implementations do not log a line at that point. So the symptom reads as "no response". What actually happened is "a response arrived and its key was not in my table".

Byte order gets blamed sometimes. To the code doing the matching, SystemBytes is not an integer — it is four bytes compared for equality. A flipped endianness does not produce a subtly wrong value, it produces a completely different one, and 00 00 00 42 shows up in the capture as 42 00 00 00. I have not actually met this one in the field, so treat it as a possibility rather than a known pattern.

A healthy session — the value comes straight back

Faults at {}. One Select, then S1F13 on the same connection.

06:03:35.061 TX Select.req   00 00 00 0A 00 0B 00 00 00 01 00 00 00 41
06:03:35.074 RX Select.rsp   00 00 00 0A 00 0B 00 00 00 02 00 00 00 41   13.2 ms
06:03:35.124 TX S1F13 W      00 00 00 1A 00 0B 81 0D 00 00 00 00 00 42 01 02 41 07 48 4F 53 54 2D 30 31 41 03 31 2E 30
06:03:35.128 RX S1F14        00 00 00 37 00 0B 01 0E 00 00 00 00 00 42 01 02 21 01 00 01 02 41 15 ...   3.7 ms
messageSessionID (0-1)byte 2byte 3SType (5)SystemBytes (6-9)
TX Select.req00 0B = 1100000100 00 00 41
RX Select.rsp00 0B = 110000 = accepted0200 00 00 41, unchanged
TX S1F13 W00 0B = 1181 = W-bit 1, Stream 10D = Function 1300 data00 00 00 42
RX S1F1400 0B = 1101 = W-bit 0, Stream 10E = Function 1400 data00 00 00 42, unchanged

Read it in this order. The first four bytes 00 00 00 0A say ten bytes follow, and control messages carry no body, so it is always ten. S1F13 has a body, so it is 00 00 00 1A — 26 bytes. On a data message the top bit of byte 2 is the W-bit, which makes 81 mean "Stream 1, reply expected", and the S1F14 answering it drops that bit to 01. In both pairs, bytes 6-9 came back exactly as sent. The host finds its entry, clears it, and moves on.

The mismatched session — exactly one byte

Same equipment, same request, with only the wrongSystemBytes fault on. That knob adds one to the request's SystemBytes and returns that.

06:03:35.589 TX Select.req   00 00 00 0A 00 0B 00 00 00 01 00 00 00 42
06:03:35.591 RX Select.rsp   00 00 00 0A 00 0B 00 00 00 02 00 00 00 43   1.9 ms
                                                          ^^^^^^^^^^^
messageSessionID (0-1)byte 2byte 3SType (5)SystemBytes (6-9)
TX Select.req00 0B = 1100000100 00 00 42 sent
RX Select.rsp00 0B = 110000 = accepted0200 00 00 43 received

Structurally this is a flawless Select.rsp. SType 02, SessionID matches, Select Status 0, same length. One of the four bytes at 6-9 is off: 43 where 42 went out. So the pending reply table's 0x00000042 entry is never touched, and the 0x00000043 that arrived belongs to nobody.

The equipment side logs a perfectly normal TX Select.rsp at 06:03:35.591. The host side logs nothing. That is why putting the two logs side by side does not reveal the cause — each side looks correct from where it is standing.

One more thing about this knob: it rewrites the Select.rsp only. With the fault still on, sending S1F13 over the same connection got SystemBytes 00 00 00 44 back unchanged. The data-message path is untouched, and that is as far as this capture proves anything. Whether a real tool's mismatch stays inside the control transaction or spreads to data messages depends on its stack.

Which timer is actually running, T3 or T6

This is the part I had wrong before. What the host is waiting on depends on what it sent.

  • Select, Linktest and Separate are E37 control transactions. The timer that expires there is T6 (control transaction timeout, 5 s default). With a Select.rsp whose SystemBytes are off, as above, the host sits until T6 runs out and then logs a select timeout.
  • A data message like S1F13 expires on T3, the reply timeout E5 and E30 use — typically 45 s, range 1-120 s. A no reply that lands 45 seconds in is a data transaction, not a control one.

The elapsed time in the log is enough to tell them apart on its own. Five seconds is T6, forty-five is T3. What each timer does on expiry is in which HSMS timer just fired.

When T3 expires on a data transaction, E5 has S9F9 (transaction timer timeout) in Stream 9, whose SHEAD body carries the header of the unanswered primary message. The whole header means the SystemBytes ride along with it — there is no better evidence for catching a mismatched value. Whether the other side actually sends S9F9 is implementation-dependent, and this capture does not confirm it; why you should not design around it is in its own article. A header so broken that it will not decode is S9F7 (illegal data), not S9F9.

Telling silence from indifference

The first split to make on site is between these two:

  • Nothing arrives. TCP is up but no reply frame exists. The equipment can't service the Select — wrong state, a SessionID it doesn't recognise, or a session it hasn't released.
  • It arrived and was ignored. The frame reached the host and the host dropped it. Mismatched SystemBytes is the classic case.

There's only one way to split them: stop reading the application log and look at the socket.

tcpdump -i any -nn -X port <equipment HSMS port>

If the frame is there, it's a host problem. If it isn't, it's an equipment problem. That one line saves two meetings with the vendor.

Time does the same job. The mismatched reply above came back in 1.9 ms. Silence gives you nothing after seconds of waiting. The application log writes the same timeout line for both, but at the socket level one is an immediate frame and the other is nothing at all.

After one mismatched reply I kept listening for another 16 seconds.

06:04:53.897 TX Select.req   00 00 00 0A 00 0B 00 00 00 01 00 00 00 81
06:04:53.899 RX Select.rsp   00 00 00 0A 00 0B 00 00 00 02 00 00 00 82   2.2 ms
             (then 16,016.1 ms with no frame, and the peer never closed the socket)

On E37's defaults both T6 (5 s) and T7 (10 s) had already run out in that window. This simulator runs neither timer — read that as evidence that a simulator is not a substitute for the standard. Real equipment should have dropped the connection by then. The 16,016.1 ms is my client's own patience, not a value of T6 or T7.

It's worth knowing whether your host library logs this at all. Some implementations record discarded frames at debug level; others say nothing. On the second kind, this failure is invisible without tcpdump.

Where the mismatch usually comes from

The cause is normally on the equipment side, and the common shape is a reply built with a freshly generated SystemBytes instead of a copy of the request's. It shows up on tools with a hand-rolled GEM stack or a wrapper over a native protocol. From the vendor's side "we sent a response" is genuinely true, which is why the conversation does not move until you attach the capture.

The accident runs the other way too. A host that shares one counter across sessions can put two open transactions on the same SystemBytes, and then even a perfectly compliant echo leaves it unable to tell which answer is which. That is not the equipment's fault, and it has its own walkthrough.

Reproducing it

The captures above came from the SECS/GEM simulator. Start a passive listener, connect to it with a socket from outside, and you get the healthy session as printed; set the equipment's wrongSystemBytes fault and you get the broken one. Turning the fault back off restores the echo, which I verified before finishing:

06:05:10.378 TX Select.req   00 00 00 0A 00 0B 00 00 00 01 00 00 00 91
06:05:10.381 RX Select.rsp   00 00 00 0A 00 0B 00 00 00 02 00 00 00 91   3.4 ms
06:05:10.431 TX S1F13 W      00 00 00 1A 00 0B 81 0D 00 00 00 00 00 92 ...
06:05:10.432 RX S1F14        00 00 00 37 00 0B 01 0E 00 00 00 00 00 92 ...   0.7 ms

If you're still at the Select stage and don't yet know which of the failure modes you have, start with three failures that look identical to the host instead.

Next time you see no reply, reach for tcpdump before the network team — and read bytes 6-9 first.