Skip to content
AutoST by Zyberum GmbH
Menu
UDSNRC

UDS negative response codes explained for testers

What a UDS negative response (0x7F) tells you on the bench: the structure, the codes that matter during enumeration and SecurityAccess, and how to read them.

Tom Zaubermann · Published · 8 min read

A UDS negative response is the ECU telling you it will not do what you asked, and the reason why. It is always three bytes: 7F, the service identifier (SID) of the request that was rejected, and a negative response code (NRC). Read correctly, those codes map the ECU for you, because an ECU that rejects a request precisely is also describing its own structure one answer at a time.

The structure, byte by byte

Send a request and you get one of three outcomes. A positive response starts with the SID plus 0x40: a request 22 F1 90 (ReadDataByIdentifier, DID 0xF190) answered with 62 F1 90 ... is a success. A negative response looks like this:

7F 22 31
│  │  └─ NRC: requestOutOfRange
│  └──── SID that was rejected: 0x22 ReadDataByIdentifier
└─────── negative response service identifier

The third outcome is silence, which on CAN usually means the ISO-TP (ISO 15765-2) frame was never accepted, the arbitration ID is wrong, or the ECU is busy. Silence is not an NRC, and you should treat it differently from an explicit rejection.

The codes that carry the most information

ISO 14229-1 Annex A defines the full table. During a scan, a handful of codes do most of the work:

CodeISO nameWhat it tells the tester
0x10generalRejectThe ECU refuses without a reason. Some implementations answer this to everything they do not understand, which hides structure.
0x11serviceNotSupportedThe service does not exist on this ECU at all.
0x12subFunctionNotSupportedThe service exists, the sub-function does not.
0x13incorrectMessageLengthOrInvalidFormatLength or format wrong. Useful signal while fuzzing.
0x22conditionsNotCorrectThe service exists but preconditions are unmet (wrong state, speed, voltage).
0x31requestOutOfRangeThe parameter, usually a DID or RID, is unknown or out of range.
0x33securityAccessDeniedThe service is gated behind SecurityAccess (0x27).
0x35invalidKeyWrong key sent to SecurityAccess.
0x36exceedNumberOfAttemptsToo many wrong keys; the lockout has tripped.
0x37requiredTimeDelayNotExpiredYou must wait before trying again.
0x7EsubFunctionNotSupportedInActiveSessionSub-function exists, not in this session.
0x7FserviceNotSupportedInActiveSessionService exists, not in this session.
0x78requestCorrectlyReceived-ResponsePendingNot an error: wait for the real answer.

Telling “not here” from “not now”

The single most useful distinction during enumeration is between a service that does not exist and one that exists but is locked behind a session or SecurityAccess. The codes make it explicit.

  • 0x11 serviceNotSupported: the SID is absent. Stop probing it.
  • 0x7F serviceNotSupportedInActiveSession: the SID is present, but you are in the wrong session. Re-send after 10 03 (extended) or, carefully, 10 02 (programming).
  • 0x33 securityAccessDenied: the SID is present and reachable in this session, but needs an unlocked SecurityAccess level first.

So a RoutineControl probe that returns 7F 31 33 is not a dead end. It is confirmation that 0x31 exists here and that something worth protecting sits behind 0x27. That one byte redirects the whole test toward the seed/key gate.

Response pending, and the ECUs that abuse it

0x78 is the ECU asking for more time. The correct tester behaviour is to reset its P2* timer and keep waiting for either a positive response or a different NRC. Byte-for-byte:

tx 31 01 FF 00    RoutineControl startRoutine 0xFF00
rx 7F 31 78       response pending, keep waiting
rx 7F 31 78       still working
rx 71 01 FF 00    final positive response

Two patterns are findings in their own right. An ECU that sends 0x78 and never follows up hangs any tester that honours the protocol. An ECU that sends an unbounded stream of 0x78 can be used to keep a session, or even a programming session, alive far longer than intended.

Using NRCs during SecurityAccess

The SecurityAccess codes form a small state machine you can watch. A healthy gate moves through them:

tx 27 01          requestSeed level 1
rx 67 01 A1 B2    seed returned
tx 27 02 00 00    sendKey (wrong)
rx 7F 27 35       invalidKey
...repeat...
rx 7F 27 36       exceedNumberOfAttempts (lockout tripped)
tx 27 02 00 00    sendKey
rx 7F 27 37       requiredTimeDelayNotExpired

An ECU that answers 0x35 forever, never escalating to 0x36 and 0x37, has no attempt counter and no delay. That is a weak gate, and the NRC trail is the evidence.

Where the codes mislead

Not every ECU uses the table cleanly. Some answer 0x10 generalReject to anything unfamiliar, erasing the difference between “not supported” and “not in this session”. Some return 0x22 conditionsNotCorrect where 0x31 would be correct. A single response is therefore weak evidence. The reliable method is statistical: probe each SID across every discovered session and compare the response codes, so a mislabelled ECU still reveals its real map. AutoST’s enumeration does exactly this, classifying every service from its response pattern rather than trusting one answer, and our free UDS NRC decoder resolves a single code when you just need the name.

FAQ

Frequently asked questions

What is the structure of a UDS negative response?

Three bytes: 0x7F, the service identifier of the rejected request, and the negative response code. A tester that sends 22 F1 90 and receives 7F 22 31 learns the service exists but the data identifier is out of range.

Is response pending (0x78) a failure?

No. requestCorrectlyReceived-ResponsePending tells the tester the ECU needs more time, so the tester extends its P2* timeout and waits. An ECU that sends 0x78 and then never answers is itself a finding, because it hangs the tester.

Why do ECUs answer 0x31 for an unknown DID instead of 0x7F?

requestOutOfRange (0x31) is the correct answer to a data identifier the ECU does not support. serviceNotSupportedInActiveSession (0x7F) means the whole service is unavailable in the current session. Confusing the two is common and makes enumeration harder.

Call usBook a demo

Pick a time that suits you

Open in a new tab