Skip to Content
UDS Simulator 2.0 Released
DocsLearnUDS Best Practices — Error Handling and NRC Strategies

Best Practices

Module 5 of 5 · 1 lesson · ~15 min · ← Back to Learning Hub


Lesson: Error Handling and NRC Strategies

Writing a UDS client that works in the lab is straightforward. Writing one that holds up in production — across dozens of ECU variants, edge cases, and real-world communication errors — requires deliberate error handling.

The NRC Taxonomy

When a request fails, the ECU always responds with:

7F [SID] [NRC]

NRCs fall into three categories:

Category 1 — Retry-safe (transient, try again)

NRCNameAction
0x78requestCorrectlyReceivedResponsePendingWait and poll — final response is coming
0x37requiredTimeDelayNotExpiredWait the full delay (usually 10 s), then retry

Category 2 — Fix required (something is wrong with your request or state)

NRCNameAction
0x12subFunctionNotSupportedWrong sub-function — check spec
0x13incorrectMessageLengthOrInvalidFormatWrong byte count — fix request
0x22conditionsNotCorrectEnter correct session or meet preconditions
0x24requestSequenceErrorOperations out of order — reset and retry from start
0x31requestOutOfRangeParameter outside valid range
0x33securityAccessDeniedUnlock via SID 0x27 first
0x35invalidKeySecurity key calculation error
0x36exceededNumberOfAttemptsStop — wait for delay, then restart sequence

Category 3 — Hard failures (not recoverable without investigation)

NRCNameAction
0x10generalRejectECU rejected request — check documentation
0x11serviceNotSupportedSID not implemented in this ECU
0x7EsubFunctionNotSupportedInActiveSessionChange session first
0x7FserviceNotSupportedInActiveSessionChange session first

Implementing Retry Logic

A robust retry handler should distinguish between these categories:

async function sendUDSRequest(request, { maxRetries = 3, pendingTimeout = 5000 } = {}) { const deadline = Date.now() + pendingTimeout; for (let attempt = 0; attempt < maxRetries; attempt++) { const response = await transport.send(request); // Positive response if (response[0] !== 0x7F) return response; const nrc = response[2]; // NRC 0x78: Response Pending — ECU is processing, wait for final response if (nrc === 0x78) { if (Date.now() >= deadline) throw new UDSError('P2* timeout exceeded', nrc); await delay(50); // brief pause, then listen again continue; } // NRC 0x37: Time delay — back off significantly if (nrc === 0x37) { await delay(10_000); continue; } // All other NRCs are not retryable at this level throw new UDSError(getNRCDescription(nrc), nrc); } throw new UDSError('Max retries exceeded'); }

Handling NRC 0x78 Correctly

NRC 0x78 is not an error — it is the ECU’s way of saying “I received your request, I need more time, don’t timeout yet.” This is common during:

  • Memory erase operations (31 01 FF 00)
  • Flash programming blocks (36 [seq] [data])
  • Complex routine executions (31 01 [routine])

The correct behavior is to keep the transport connection open and wait for the actual response within the P2* window.

Common mistake: Treating 0x78 as a failure and resending the request. This resets the ECU’s internal operation and may corrupt the flash.

Logging for Diagnostics

Every UDS exchange should be logged with enough context to diagnose issues post-deployment:

function logExchange(request, response, durationMs) { const entry = { timestamp: new Date().toISOString(), requestHex: toHex(request), sid: request[0], responseHex: toHex(response), success: response[0] !== 0x7F, nrc: response[0] === 0x7F ? response[2] : null, durationMs, }; auditLog.write(entry); }

Log at minimum:

  • Timestamp
  • Full hex of request and response
  • Duration (to identify timing issues)
  • Session state at time of request
  • ECU identifier / target address

Graceful Degradation

Design your UDS client to fall back when a service fails:

async function getVehicleSpeed() { try { // Primary: Read Data By Identifier (most ECUs) const response = await sendUDSRequest([0x22, 0x01, 0x01]); return parseSpeed(response); } catch (err) { if (err.nrc === 0x31) { // DID not supported — try alternate DID return getVehicleSpeedAlternateDID(); } throw err; // Re-throw if not a known fallback condition } }

Common graceful degradation patterns:

  • If SID 0x22 DID fails: try SID 0x23 (Read Memory By Address) if address is known
  • If Extended Session fails: attempt limited operations in Default Session
  • If Security Access fails: skip protected operations, continue with unprotected diagnostics

User-Facing Error Messages

Never expose raw NRCs or hex dumps to end users. Map them to meaningful messages:

const NRC_MESSAGES = { 0x22: 'This operation is not available. Ensure the vehicle is stationary and ignition is on.', 0x33: 'Authentication required. Please complete the security access procedure.', 0x35: 'Authentication failed. Check your credentials and try again.', 0x36: 'Too many failed attempts. Please wait before retrying.', }; function getUserMessage(nrc) { return NRC_MESSAGES[nrc] ?? 'An unexpected diagnostic error occurred. Please check the vehicle connection.'; }

Session Management Best Practices

  • Enter the lowest session that meets your needs. Extended session has more access than Default, but also adds complexity (S3 timeout, Tester Present requirement).
  • Always exit cleanly. Send 10 01 when done — do not just disconnect.
  • Monitor S3 actively. Set a timer; send 3E 80 (suppressed Tester Present) every 2 seconds when in a non-default session.
  • Handle session loss gracefully. If 0x7F with NRC 0x7F is received unexpectedly, the session expired — re-enter and retry.

Production Checklist

Before shipping a UDS client to production:

  • All NRCs categorized and handled (retry-safe vs. fix-required vs. hard-fail)
  • NRC 0x78 handled as informational — no re-send, just wait
  • Retry logic with configurable backoff
  • P2* deadline enforced (don’t wait forever for pending responses)
  • Full hex logging of every exchange
  • User-friendly error messages (no raw hex to end users)
  • Graceful degradation for unsupported DIDs or services
  • Session timeout monitoring with automatic Tester Present
  • Comprehensive test coverage: both success and each failure NRC

Testing Error Conditions

Test your error handling explicitly — don’t assume it works:

  • NRC 0x22: Send a write request in Default session
  • NRC 0x33: Send a protected request without security unlock
  • NRC 0x35: Send a deliberately wrong security key
  • NRC 0x78: Trigger a long operation (memory erase) and verify your client waits correctly
  • NRC 0x7F: Drop to Default session, then send an Extended-session-only request
  • Timeout: Disconnect the transport mid-request and verify your client recovers

Congratulations

You’ve completed the UDS Learning Hub. You now understand:

  • ISO 14229 protocol fundamentals — message structure, NRCs, positive/negative responses
  • Timing parameters — P2, P2*, S3, and how to use them correctly
  • Core services — Session Control (0x10), Security Access (0x27), Read DTC (0x19)
  • Real-world workflows — complete DTC diagnosis from session entry to verification
  • Production patterns — retry logic, NRC categorization, logging, graceful degradation

Where to Go Next