Field note
Kia UDS $19 02 FF: a code catalogue, not a fault list
A 2018 Optima with one stored code came back with 25, one flagged MIL-on. The reply was a code catalogue, and 13 lost CAN frames shifted the rest.
2018 Kia Optima
The car was a 2018 Kia Optima with the 2.4 GDI engine. The MIL was off, and the car had one stored code, P200A, an intake manifold runner code. Lane Pilot's first scan of it reported 25 codes, and one of them, C00AA, came back marked confirmed, failed since clear, and MIL on. A chassis code on an engine computer, with the lamp on, on a car whose lamp was off.
Two separate mistakes produced that, plus a third that was hiding behind a correct-looking answer. Any tool that reads UDS service 19 can make all three, so here they are with the bytes.
Mistake 1: 19 02 FF returns a catalogue
19 02 FF is ReadDTCInformation, sub-function 02 (report codes by status mask), with a mask of FF. The module returns every code whose status byte shares a bit with the mask. A mask of FF matches every bit, including the ones that only mean "this test has not run yet".
So the reply is not a list of faults. It is close to the module's whole catalogue: every code it monitors, each with a status byte. In the engine computer's replies, every record that was read in step carried status 50 or 40, and neither one has a fault bit set. 40 means the test has not completed during this drive cycle. 50 adds that it has not completed since the codes were last cleared either.
The status byte, bit by bit:
| Bit | Mask | Meaning |
|---|---|---|
| 0 | 01 | Test failed |
| 1 | 02 | Test failed this operation cycle |
| 2 | 04 | Pending |
| 3 | 08 | Confirmed |
| 4 | 10 | Test not completed since last clear |
| 5 | 20 | Test failed since last clear |
| 6 | 40 | Test not completed this operation cycle |
| 7 | 80 | Warning indicator requested |
A record is a fault only when one of the fault bits is set: status & 0x2F is not zero. A record with only bits 4 and 6 set is an entry in the catalogue, not a problem with the car.
Mistake 2: 13 frames went missing, and everything after them shifted
The reply was long. Its first frame declared 255 bytes (0FF), which takes 37 CAN frames. An ELM327 prints a reply like that as numbered lines: 0: for the first frame, then 1: onward for the follow-on frames, which carry ISO-TP's sequence counter. The counter runs up to F and then wraps back to 0.
In the capture, the lines ran 0 through 9, A, B, C, and then the next line to arrive was an A from the second lap. Thirteen frames never arrived. The reassembly code at the time ignored the counter and joined lines in the order they came in, so it glued the 24 surviving frames together as if nothing was missing.
Codes in this reply are 4-byte records: three bytes of code and one status byte. Thirteen frames is 91 bytes, which is not a multiple of four, so every record after the gap was read out of phase. Two examples:
read as: 40 05 07 00 -> C0005, status 00
really: 05 07 00 40 -> P0507, status 40
read as: 40 AA AA AA -> C00AA, status AA
really: CAN padding
The real P0507 record carries status 40: not completed this cycle. The AA bytes are the padding at the end of the last CAN frame.
Read as a status byte, AA sets bits 1, 3, 5 and 7: failed this cycle, confirmed, failed since clear, and warning lamp requested. The fake MIL-on fault was padding read one byte out of phase. Every chassis code in the report came from the same shift, and every one of them appeared only after the wrap point. The scan read 41 records from that reply: 21 real ones from before the gap, one cut in two by it, and 19 out of phase after it. Together they came to the 25 codes the scan showed.
Mistake 3: "no faults" from a reply that never finished
The same scan also checked each module on its own, and for the engine computer it said: no faults, 31 monitors supported but not yet run.
That read lost frames too, but from the end: its counter ran in perfect sequence and it still delivered only 160 of its 255 bytes. So the records that did arrive were still aligned. It read 39 records, found 31 unique clean ones, and called the module healthy while 95 of the 255 declared bytes were missing.
The car's one stored code, P200A, is not in any of the 160 bytes that arrived, and not in the bytes the other read received either. Bytes 160 to 180 of the reply reached neither read. If the engine computer listed P200A at all, it was in that stretch, and "no faults" was a verdict on a reply that had not finished arriving.
The transmission computer's reply in the same scan came up short too, with 83 of its 91 bytes. A lost tail leaves no gap in the counter, so a sequence check alone cannot see it. A loss of exactly 16 or 32 frames is invisible to the counter as well. Both checks are needed: the counter and the declared length.
What the scan does now
- Reassembly keeps the frame counter. Frames are kept up to the first gap and nothing past a gap is decoded, because only bytes that arrived in order from the start are provably the module's.
- What arrived is compared with the declared length. A short reply is marked incomplete, and an incomplete reply can never produce "no faults".
- A record becomes a fault only if a fault bit is set. A code already known from the standard OBD-II reads still gets its status detail attached when the reply contains it.
- A module with lost frames is marked incomplete on screen and in the PDF, and the scan summary turns amber instead of saying "Full scan complete".
If you read 19 02 yourself
- Filter on the status:
status & 0x2F. Bits 4 and 6 mean "not tested yet", and on their own they are not a fault. - Check the frame counter, including the wrap after
F. - Check the byte count against the length in the first frame.
- If codes from another system turn up partway through a long reply, such as C-codes from an engine computer, suspect the reassembly before you suspect the car.