NFC Agent

Errors and TLS

The agent's error codes, and how devices and browsers trust its certificate.

TLS & Certificates

The agent serves wss:// with a self-signed certificate generated from a key it creates once and keeps. Nothing is installed into any trust store by default.

Native devices: pin the key

Phones, readers and other native clients should not install a certificate authority. They verify the agent by pinning its public key, reported as serverInfo.publicKeyPin at registration and handed out at pairing. The pin survives certificate reissues, which happen whenever the host's addresses change.

See Setting up an iOS or Android device for the pairing flow and the trust-evaluation code, including the two ways it commonly goes wrong.

Browsers: provide a certificate, or install a CA

A browser cannot pin, so it needs a certificate it already trusts:

  1. Provide one: point -cert / -key at a certificate for a name you control that resolves to the agent. Nothing is installed, and the browser trusts it because a public CA issued it.
  2. -install-ca: creates a local certificate authority and installs it in the system trust store. A CA there can sign for any name, not just this agent, so prefer option 1 where you can arrange it.

With -install-ca, the bootstrap server on port 9472 serves the root certificate for installation, PIN-gated.

Browsers also need their origin allowed. See Browser origins. A trusted certificate and an allowed origin are separate requirements, and a failure of either looks the same from the page.


Error Codes

Errors arrive as a response with success: false, a human-readable error string, and a structured payload:

{
  "id": "req_1",
  "type": "error",
  "success": false,
  "error": "data too large: 900 bytes exceeds tag NDEF capacity of 504 bytes",
  "payload": {
    "code": "CAPACITY_EXCEEDED",
    "retryable": false,
    "op": "WriteData",
    "tagUID": "04:A1:B2:C3"
  }
}

code has always been present and its strings are stable. retryable, op, and tagUID are additive: a client reading only code is unaffected.

retryable answers whether repeating the identical request could plausibly succeed. Combined with code it gives three distinct outcomes:

ConditionMeaningWhat a client should do
retryable: true, code ≠ TAG_REMOVEDTransient: I/O glitch, full queue, timeoutRetry, with backoff
retryable: true, code = TAG_REMOVEDThe tag left the field mid-operationAsk the user to present the tag again
retryable: falseRefused on its meritsDo not retry; surface it

Protocol errors

Raised by the bridge itself, before reaching a tag.

CodeRetryableDescription
PARSE_ERRORnoMessage was not valid JSON
INVALID_PAYLOADnoPayload did not match the message type
INVALID_REQUESTnoRequired field missing or invalid
INVALID_MESSAGE_TYPEnoMessage type not valid at this point in the exchange
UNKNOWN_TYPEnoUnrecognized message type
INVALID_DEVICEnoDevice ID did not match the connection
TAG_MISMATCHnoThe tag present is not the one the request named
TAG_NOT_NAMEDnoRequest named no tag and did not ask for one to be guessed
REGISTRATION_FAILEDnoDevice could not be registered
SESSION_LOCKEDnoAnother client holds the session
TAG_SEND_FAILEDyesTag data could not be delivered internally
READ_ERRORyesFailed to read from the connection
TIMEOUTyesOperation timed out
DEVICE_GONEnoTarget device disconnected
INTERNAL_ERRORyesUnexpected agent-side failure
BUSYyesEarlier work has not finished: a reader completing an operation its caller abandoned, or more requests outstanding than the connection queues
UNKNOWN_ERRORnoUnclassified: never advertised as retryable

NFC errors

Something happened at the tag. These mirror the agent's internal error codes.

CodeRetryableDescription
NOT_SUPPORTEDnoTag or device does not support the operation
TAG_REMOVEDyesTag left the field mid-operation
AUTH_FAILEDnoAuthentication failed: the same key will fail again
READ_FAILEDyesRead failed
WRITE_FAILEDyesWrite failed
TRANSCEIVE_FAILEDyesRaw exchange failed
TAG_NOT_CONNECTEDyesNo tag connected
READ_ONLYnoTag is locked, or the agent is in read-only mode
RAW_CHANNEL_DISABLEDnoThe raw APDU channel is off; enable it to send raw exchanges
CAPACITY_EXCEEDEDnoData larger than the tag's usable NDEF capacity
INVALID_DATAnoData was malformed
MULTIPLE_TAGSnoMore than one tag in the field; separate them and try again
NO_CARDyesNothing is holding the tag the request named