Errors and debugging
What throws, what resolves with status 0, and how to see what was sent.
There are two kinds of failure, and they surface differently.
| Failure | How you see it |
|---|---|
Usage errors: no initTLS(), invalid body, unknown fetch() method, failed native download | The promise rejects with a TlsClientError. |
| Network errors: DNS, refused connection, TLS, proxy, timeout, bad certificate pin | The promise resolves with status: 0; body holds the reason. |
TlsClientError
The library's own errors are TlsClientError, exported from the package root:
try {
await new Session().get("https://httpbin.org/get"); // before initTLS()
} catch (error) {
console.log(error instanceof TlsClientError, error.message);
// true Client not initialized. Call initTLS() first.
}When it wraps another error, the original is available as error.cause.
try {
await session.post("https://httpbin.org/post", { body: { not: "a string" } });
} catch (error) {
console.log(error.message); // body must be a string or Buffer; JSON.stringify objects before sending
}status: 0
A request that never got an HTTP response still resolves, so check the status before reading the body. A small helper makes that hard to forget:
async function getJson(session, url) {
const res = await session.get(url);
if (res.status === 0) {
// DNS, TLS, proxy or timeout failure: body holds native's error text
throw new Error(`request failed: ${res.body}`);
}
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}Common body messages:
| Message contains | Cause |
|---|---|
no such host | DNS lookup failed. |
Client.Timeout exceeded | The session timeout ran out. |
certificate signed by unknown authority | Untrusted certificate. See TLS verification. |
bad ssl pin detected | Certificate pinning rejected the server. |
cannot unmarshal … | An option has the wrong type for native (e.g. a priority weight above 255). |
HTTP/3 request failed | QUIC failed during protocol racing. |
Debug output
debug: true on a session makes native print the raw request and response for every request: headers, cookies, status and bytes on the wire.
const session = new Session({ debug: true });headers on request:
map[Accept:[*/*] Accept-Encoding:[gzip, deflate, br] Connection:[keep-alive] User-Agent:[tls-client/2.2.0]]
requested https://httpbin.org/status/204 : status 204Native writes this to stdout, outside Node's console.
Common problems
- The server sees
User-Agent: tls-client/2.2.0orGo-http-client/2.0. Nouser-agentheader was sent. See Headers. - A site still blocks you. Check what it sees at tls.peet.ws: fingerprint, headers and header order should all match one browser. A mismatched
User-Agentand profile is the most common cause. initTLS()fails on Alpine. musl isn't supported yet. See Installation.- Every request is
status: 0with racing on.withProtocolRacingneeds a SOCKS5 proxy or none. See HTTP/3. - Requests hang at high volume. The threadpool is saturated. See Concurrency.