Which HSMS Timer Just Fired? Telling T3, T6, T7 and T8 Apart
One log line says timeout. A real capture separates T3 from T6, lists the SEMI E37 defaults, and shows the two timers your host must enforce itself.
When the host log holds one line that says timeout, the next thirty minutes are usually guesswork. HSMS has five timers and each one watches a different stretch of the connection. Which timer fired is the same question as where to look. A T6 expiry means the session never opened; a T3 expiry means it opened and the application never produced an answer. Nothing on the two checklists overlaps.
The five stretches do not overlap
SEMI E37 (HSMS-SS) defines five timers. The defaults and ranges below are the ones in E37's parameter table.
| Timer | E37 name | Default (range) | What it measures | What its expiry means |
|---|---|---|---|---|
| T3 | Reply Timeout | 45 s (1–120) | From sending a data message with the W-bit set until the reply arrives | The session is alive and the application failed to produce an answer |
| T5 | Connect Separation Timeout | 10 s (1–240) | Between one connection attempt and the next | Not an error. It is the minimum spacing before you retry |
| T6 | Control Transaction Timeout | 5 s (1–240) | How long a control transaction (Select, Deselect, Linktest) stays open | The session never opened, or it is not open when you thought it was |
| T7 | NOT SELECTED Timeout | 10 s (1–240) | How long the TCP connection sits in NOT SELECTED | The peer is not sending Select.req |
| T8 | Network Intercharacter Timeout | 5 s (1–120) | The gap between bytes of one message | A frame arrived half-way and stopped |
The defaults are a starting point, not an agreement. The real values are settled between host and equipment in the interface spec, and almost nobody ships the 45-second T3. Stand in front of a tool for 45 seconds waiting on an unanswered S2F41 once and you understand why most lines pull it down near 10.
Half the diagnosis is knowing whose timer it is. T3 and T6 belong to whoever sent the request — T3 for a data message, T6 for a control message. T5 is the active entity's alone. T7 belongs to the side waiting to reach SELECTED after the TCP connection comes up, which in practice is the passive equipment waiting for Select.req. T8 belongs to whichever side is holding half a message.
What happens on expiry differs too. T6, T7 and T8 are failures of the connection: close the TCP connection. T3 fails one transaction and leaves the connection alone. And the rule that a timed-out transaction is reported back with S9F9 (Transaction Timer Timeout) is SECS-II (SEMI E5), not E37 — do not go looking for S9F9 in the HSMS document.
One more confusion comes up often enough to name. T1, T2 and T4 are not HSMS timers — they are the SECS-I serial timers from SEMI E4, where T1 is inter-character, T2 protocol, T4 inter-block. HSMS inherits T3 under the same name and adds T5 through T8 on top. So if the interface spec for an Ethernet-only tool lists a T1 value, that document was copied from the serial era. Ask why the row is there before you try to agree a number for it.
The capture: start with a healthy round trip
Taken 2026-09-10 with a plain Python socket against the simulator's passive listener (127.0.0.1:5501). The hex is the bytes the equipment side logged as RX and TX; the times are measured at the host. Session ID is 00 0B (11). The raw export is committed at content/demos/hsms-timeout-timers-t3-t5-t6-t7-t8.json.
TX Select.req 00 00 00 0A 00 0B 00 00 00 01 00 00 03 01
RX Select.rsp 00 00 00 0A 00 0B 00 00 00 02 00 00 03 01 3.6 ms
The first four bytes are the length prefix (0A = 10, header only); the ten that follow are the header. Bytes 0–1 are the session ID, bytes 2–3 are zero because this is a control message, byte 4 is the PType (0 = SECS-II), byte 5 is the SType (01 Select.req, 02 Select.rsp), bytes 6–9 are the SystemBytes 00 00 03 01, handed back exactly as sent.
TX S1F13 W=1 00 00 00 1A 00 0B 81 0D 00 00 00 00 03 02
01 02 41 06 48 4F 53 54 49 44 41 04 31 2E 30 30
RX S1F14 00 00 00 37 00 0B 01 0E 00 00 00 00 03 02
01 02 21 01 00 01 02 41 15 56 58 2D 39 30 30 30 20 50 6C 61 73 6D 61 20 45 74 63 68 65 72
41 0D 53 45 43 53 47 45 4D 2D 31 2E 34 2E 31 2.4 ms
A data message, so byte 2 is 81 — top bit is the W-bit, the low seven bits are stream 1 — and byte 3 0D is function 13. The reply's byte 2 is 01: W-bit down, same stream, and the SystemBytes 00 00 03 02 unchanged. The body is 01 02 (L[2]) holding 21 01 00 (COMMACK 0) and a list of A[21] VX-9000 Plasma Etcher and A[13] SECSGEM-1.4.1. It came back in 2.4 ms, so T3 never got started.
TX Linktest.req 00 00 00 0A 00 0B 00 00 00 05 00 00 03 03
RX Linktest.rsp 00 00 00 0A 00 0B 00 00 00 06 00 00 03 03 1.2 ms
Linktest is SType 5 and 6 with no body. On a link whose live round trip is 1.2 ms, a T6 that expires at its 5-second default is not latency — it is a peer that stopped answering.
What a T6 expiry actually looks like
I set the listener's ignoreLinktest fault. Linktest.req still reaches the equipment — it is in the RX log — and only the reply is swallowed.
06:02:51.362 RX Select.req 00 00 00 0A 00 0B 00 00 00 01 00 00 04 01
06:02:51.362 TX Select.rsp 00 00 00 0A 00 0B 00 00 00 02 00 00 04 01 Select Status 0
06:02:51.364 RX Linktest.req 00 00 00 0A 00 0B 00 00 00 05 00 00 04 02
06:02:51.364 FAULT Linktest.req ignored (ignoreLinktest)
(no reply)
06:02:56.37 host gave up at T6 = 5 s and closed the connection — measured 5.005 s
The point is the RX line. The packet arrived, the session is SELECTED, and from the host it still reads as a dead link. Linktest is a control transaction, so what expires here is T6, not T3. A log that does not separate the two prints the same line for both.
Swallow the Select.req itself with silentAfterSelect and the same five seconds burn before the session ever opens.
06:02:56.381 RX Select.req 00 00 00 0A 00 0B 00 00 00 01 00 00 05 01
06:02:56.381 FAULT Select.req ignored (silentAfterSelect)
(no reply)
06:03:01.39 host gave up — measured 5.005 s
TCP is up and there is no Select.rsp: this is NOT SELECTED. A host that logs T3 here does not have a timer problem, it has a state-machine bug. Both faults were reset to {} as soon as the capture was taken.
A T3 expiry is not something this listener can produce. It answers every data message, and when it has no answer it still returns SxF0 — function 0, a transaction abort. Under E5 that is a reply, and it stops T3 the moment it lands. In the field too, silence and an SxF0 are different events. What this capture proves about T3 stops at the S1F13 round trip above.
The two nobody else will enforce for you
T7 and T8 I tested directly, and both came back the same way.
I connected the TCP socket, never sent Select.req, and held it for 30 seconds. No FIN arrived, and the Select.req I sent afterwards was accepted normally.
06:03:01.391 TCP connected (127.0.0.1)
... 30.0 s with nothing sent, and no FIN ...
06:03:31.417 RX Select.req 00 00 00 0A 00 0B 00 00 00 01 00 00 06 01
06:03:31.417 TX Select.rsp 00 00 00 0A 00 0B 00 00 00 02 00 00 06 01
Three times the T7 default spent idle and the connection is still there. This listener does not enforce T7.
The second test split a frame in half: the first 6 bytes of a 14-byte Select.req, a 20-second pause, then the remaining 8.
06:03:31.418 TCP connected
TX (first 6) 00 00 00 0A 00 0B
... 20.0 s of nothing ...
TX (last 8) 00 00 00 01 00 00 07 01
06:03:51.418 RX Select.req 00 00 00 0A 00 0B 00 00 00 01 00 00 07 01
06:03:51.418 TX Select.rsp 00 00 00 0A 00 0B 00 00 00 02 00 00 07 01
Twenty seconds between the first and last byte of one frame, four times the T8 default, and it accepted the frame and answered with Select.rsp. In the article on HSMS length headers and TCP framing I wrote that whether this simulator enforces T8 was something I had not verified. Now it is verified. It does not.
That is one simulator's behaviour, not a claim about real equipment. From the host side the conclusion is the same either way: T7 and T8 are timers you run, not timers the peer runs for you. A peer that ignores T7 leaves dead sockets parked in NOT SELECTED forever; one that ignores T8 leaves half a message sitting in your parser buffer forever. Both are code that goes into the host without a discussion. The parser side of it is in the framing article above.
One log line decides the whole investigation
A log that prints seven characters of timeout makes the table above useless. Four things belong on that line, minimum:
- The timer that expired —
T3orT6, by name - The SystemBytes being waited on — the only way to find the request in a capture
- The SxFy, or the SType — this is where data and control part company
- The HSMS state at the time — a T3 in NOT SELECTED is a sequencing bug, not a timer problem
Without the SystemBytes you cannot tell real silence from a reply that came back with the wrong SystemBytes using the log alone. You have to take the capture again. A one-line commit, repaid with a reproduction.
And if T3 fired rather than T6, the session is already open, which puts the problem on the GEM side rather than in HSMS — SELECTED with no answer to S1F13 is exactly that spot.
Before touching any timer value, make sure the log can tell you which timer expired. If raising T3 to 45 seconds makes the symptom disappear, nothing was fixed; it was deferred. Everything above reproduces with one socket against the passive listener in the SECS/GEM simulator.