Why Your S2F41 Comes Back HCACK 2 While the Tool Screen Says ONLINE
S2F41 START rejected with HCACK 2 while the equipment screen reads ONLINE. Separating the SEMI E30 communication state from the three control states, and checking all of it with one command.
The host sends S2F41 with RCMD START. S2F42 comes back in 1 ms. HCACK is 2. The person standing at the tool says the screen reads ONLINE. Thirty minutes of the meeting disappear into that gap.
HCACK 2 in SEMI E5 means "cannot perform now". Not "no such command", not "bad parameter". It means wrong state. And the word ONLINE printed on the equipment screen is not the state SEMI E30 is talking about.
E30 has two state machines, and they answer different questions.
Two machines, two questions
The communication state answers "is a SECS-II conversation open?" It has exactly two values, NOT COMMUNICATING and COMMUNICATING. The host sends S1F13, the equipment answers S1F14 with COMMACK 0, and the state moves to COMMUNICATING. A dropped socket or a Separate.req puts it back.
The control state answers "who is giving this tool orders right now?" It has three values: OFF-LINE, ON-LINE LOCAL, ON-LINE REMOTE. Only the last one accepts host commands.
The two are ordered rather than independent — with no conversation open there is nothing to ask about control. That is what splits the symptom three ways.
| Current state | Send S2F41 and you get | The screen usually says |
|---|---|---|
| NOT COMMUNICATING (before S1F13) | S2F0 — a function-zero abort. The request is unmade | nothing at all |
| COMMUNICATING + OFF-LINE | S2F42, HCACK 2 | OFFLINE, or blank |
| COMMUNICATING + ON-LINE LOCAL | S2F42, HCACK 2 | ONLINE — this is the trap |
| COMMUNICATING + ON-LINE REMOTE | S2F42, HCACK 0 | ONLINE |
Row three is the one that stretches the meeting. An operator turns the tool's panel to LOCAL and the equipment is still online — the screen still writes ONLINE — but every host command now bounces with HCACK 2. The operator touched nothing they consider relevant; the host is watching something that worked yesterday stop working.
If you need the acknowledge codes themselves, the E5 table is in what HCACK 0 really tells you. Here only two of them matter: 0 and 2.
Do not read SxF0 as "no reply"
A message sent before S1F13 is not rejected, it is aborted. The equipment answers on the same stream with function 0 — S2F0 for an S2F41, S1F0 for an S1F3.
Host drivers misread this as a T3 timeout more often than you would expect. Function 0 carries no body and no W-bit, so a driver that never inspects the function field and treats "not the S2F42 I wanted" as "no answer" waits out 45 seconds and then goes hunting a network fault that does not exist. The socket was fine and the equipment replied in 1 ms.
If HSMS reaches selected and things still stop here, the split is covered in selected but not communicating.
The messages that move the control state
Two of them, from the host side.
- S1F17 Request ON-LINE → the equipment answers S1F18 ONLACK. 0 means accepted; 2 is commonly used for "already online".
- S1F15 Request OFF-LINE → S1F16 OFLACK. This is the host standing down on purpose — worth sending before maintenance so no host command lands mid-job.
Reading ONLACK 2 as "already online" is the widespread interpretation, but this run did not verify it — treat it as unverified.
There is a trap inside S1F17. A successful S1F17 does not tell you which online state you landed in. Some equipment answers ONLACK 0 and goes to ON-LINE LOCAL because the panel is set to LOCAL. The host log records "went online" and the next S2F41 comes back HCACK 2. So the thing that decides whether you are really remote is not the ONLACK — it is the HCACK on an actual S2F41.
Six rows from one command
Below is the output of a run just now against the HSMS listener behind the SECS/GEM simulator. Real bytes over a real socket, not a synthesised transcript.
npx secs-gem-host connect <host>:<port> state --report
Step Sent Expect Got Ack Time(ms) Result Note
------------------------------------------------------------------------
1 S1F1 S1F0 S1F0 - 1/45000 PASS aborted before S1F13
2 S1F13 S1F14 S1F14 0 1/45000 PASS
3 S1F1 S1F2 S1F2 - 0/45000 PASS 2 items
4 S2F41 S2F42 S2F42 2 1/45000 PASS HCACK 2 while not ON-LINE REMOTE
5 S1F17 S1F18 S1F18 0 1/45000 PASS
6 S2F41 S2F42 S2F42 0 1/45000 PASS accepted ON-LINE REMOTE
------------------------------------------------------------------------
E30 state rules: 6/6 passed
Those six rows execute the table above.
Step 1 throws an S1F1 before S1F13. S1F0 is the pass. If a normal S1F2 comes back instead, that equipment is not gating on communication state at all, which is itself worth writing down.
Step 2 opens the gate. Step 3 sends the same S1F1 again and checks that it now gets a real S1F2 — same message, different state, different outcome. Those two rows together are the evidence that the communication state exists.
Step 4 is why this article exists. HCACK 2 is the pass. An S2F41 sent to equipment that is not online yet, answered with HCACK 0, means that tool executed a host command without checking its control state — an operator can be standing at the panel while the host moves a wafer.
Step 5 sends S1F17. Step 6 sends the identical S2F41 and it goes through with HCACK 0. The 2 → 0 in the Ack column is the whole conclusion of the run.
Run it twice and step 4 breaks
Run the same command again against equipment you just ran it on and you get this.
4 S2F41 S2F42 S2F42 0 1/45000 FAIL HCACK 0 — tool was already ON-LINE REMOTE; put it OFF-LINE and rerun
------------------------------------------------------------------------
E30 state rules: 5/6 passed
Not a bug. Step 5's S1F17 left the tool in ON-LINE REMOTE, and the control state survives the socket. Reconnect the TCP and the communication state resets to NOT COMMUNICATING while the control state stays exactly where it was.
That is the second-biggest time sink here. Reconnecting does not reset the control state. A startup sequence written on the assumption that a restarted host meets fresh equipment falls apart against a tool still sitting in whatever state last night left it. Sending one unconditional S1F17 after S1F13 is cheaper than reasoning about it.
It works in the other direction too: this simulator moves the control state back to OFF-LINE when it receives S1F15. The run above contains no such step, so that behaviour is read from the code, not proven by this capture.
This is the core model; real tools carry more
What is above is the skeleton of E30. Real equipment stacks sub-states on top of it: HOST OFF-LINE separated from EQUIPMENT OFF-LINE, an ATTEMPT ON-LINE state entered at power-up while the tool tries to reach the host, and the retry timers that govern that attempt. Vendor documents name them differently.
The simulator has none of that. Two machines, no sub-states, that is the whole model. So those six rows prove that your host driver honours the core rules — they prove nothing about how a given tool will behave. Sub-state transitions on real equipment cannot be backed by a capture here, so they stay unverified. When you do get equipment, the right move is to fire one S2F41 from each sub-state and record the HCACK in a table.
For the full ten-step startup — S1F13 through S2F31, same style of run — see proving a full GEM startup handshake.
Next time you see HCACK 2
- Read the HCACK byte in S2F42, not the tool's screen. 2 is a state problem; 1 means that RCMD does not exist at all.
- Send S1F17 and read the ONLACK. Even a 0 has proven nothing yet.
- Send S2F41 again. HCACK 0 and you are done; another 2 and the panel is in LOCAL — at which point the next step is a phone call, not a code change.
The simulator's GEM Control panel shows OFF-LINE / ON-LINE LOCAL / ON-LINE REMOTE as they stand, and an S1F17 or S1F15 arriving from your host moves that display live while every byte lands in the packet list.
The SECS/GEM simulator is up right now.
npx secs-gem-host connect <host>:<port> state --report