Error codes

Every failure inside the Nen layer carries a stable ISO-xxxx code. Paste the code; this page tells you exactly what happened and the fix. Each code has its own anchor — deep-link straight to it, e.g. /docs/error-codes#iso-3001.

The wire/HTTP body is always { "error": { "code", "message" } } — a safe, generic message only. The precise diagnosis below is logged server-side as a hint and is never sent over the wire. Codes are a stable contract: never reused or renumbered. The single source of truth is ERROR_CODES.md, mirrored by both SDK errors.ts catalogs.

1xxxHandshake / key exchange

ISO-1001HTTP 400HANDSHAKE_MISSING_PUBLIC_KEY

Handshake body had neither `pk` nor `publicKey`. Client SDK out of date, or the request wasn't made by an Nen client.

ISO-1002HTTP 500HANDSHAKE_FAILED

ML-KEM encapsulation/decapsulation threw. Malformed/wrong-length public key, or a Wasm load failure.

ISO-1003HTTP 503HANDSHAKE_NETWORK

Couldn't reach /api/nen/handshake. Wrong serverUrl, server down, or CORS.

ISO-1004HTTP 502HANDSHAKE_BAD_RESPONSE

Handshake responded non-2xx or without sid/ct. The route isn't wired to handleHandshake().

2xxxSession lifecycle

ISO-2001HTTP 409SESSION_NOT_INITIALIZED

nenFetch/nenStream called before a successful handshake().

ISO-2002HTTP 401SESSION_INVALID_OR_EXPIRED

Server has no entry for X-Nen-Session. Expired by TTL, evicted, or this node never saw the handshake (use a shared/stateless store).

ISO-2003HTTP 401SESSION_HEADER_MISSING

No X-Nen-Session header. Not an Nen client, or a proxy stripped it.

3xxxAuthentication (HMAC / identity)

ISO-3001HTTP 401AUTH_SIGNATURE_MISSING

No X-Nen-Signature on a session that requires HMAC. HMAC is mandatory — this is the auth-downgrade guard.

ISO-3002HTTP 401AUTH_SIGNATURE_INVALID

HMAC over METHOD\nPATH\nTIMESTAMP\nNONCE didn't match. Tampered request, wrong key, or a canonical-string mismatch (commonly path-vs-full-URL).

ISO-3003HTTP 401AUTH_TIMESTAMP_OUT_OF_WINDOW

X-Nen-Timestamp is >30s from server time. Clock skew or a replayed/delayed request.

ISO-3004HTTP 401AUTH_IDENTITY_SIGNATURE_INVALID

Optional ML-DSA identity signature over the ephemeral key didn't verify. Wrong identity key or a MITM at handshake.

4xxxCryptography (AEAD / payload)

ISO-4001HTTP 400CRYPTO_DECRYPT_FAILED

ChaCha20-Poly1305 AEAD tag verification failed. Tampered/truncated ciphertext, or a desynced shared secret (try rotate()).

ISO-4002HTTP 500CRYPTO_ENCRYPT_FAILED

AEAD sealing of the response threw. Usually a corrupt/missing shared secret.

ISO-4003HTTP 400CRYPTO_PAYLOAD_NOT_JSON

Decryption succeeded but the plaintext wasn't valid JSON.

5xxxReplay / nonce

ISO-5001HTTP 409REPLAY_NONCE_REUSED

This nonce was already seen for the session. A legitimate identical retry, or an actual replay.

6xxxWire format / encoding

ISO-6001HTTP 400WIRE_INVALID_PAYLOAD_FORMAT

Body was missing the (ct, n) base64 pair. Not an Nen payload, or a corrupted/truncated body.

ISO-6002HTTP 400WIRE_DECODE_FAILED

base64 decode of ct/n/pk failed. Truncated by a proxy, or non-base64 data.

7xxxStreaming

ISO-7001HTTP 502STREAM_MISSING_NONCE_HEADER

Stream response had no X-Nen-Stream-Nonce. The route didn't use withNenStream(), or a proxy stripped it.

ISO-7002HTTP 502STREAM_REQUEST_FAILED

Stream response was non-ok or had no body. Upstream handler errored before streaming.

9xxxInternal / unknown

ISO-9000HTTP 500INTERNAL

Unclassified failure wrapped by NenError.from(). The original error is in the logged detail.

Programmatic lookup

Both SDKs export a reverse lookup so tooling and support can resolve a code from a log:

import { describeNenCode } from '@withnen/server'; // or '@withnen/client'

describeNenCode('ISO-3001');
// → { code: 'ISO-3001', status: 401, message: '…', hint: '…' }

See also the protocol spec and the threat model.