node-tls-clientLatest version on npm

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.

FailureHow you see it
Usage errors: no initTLS(), invalid body, unknown fetch() method, failed native downloadThe promise rejects with a TlsClientError.
Network errors: DNS, refused connection, TLS, proxy, timeout, bad certificate pinThe 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 containsCause
no such hostDNS lookup failed.
Client.Timeout exceededThe session timeout ran out.
certificate signed by unknown authorityUntrusted certificate. See TLS verification.
bad ssl pin detectedCertificate pinning rejected the server.
cannot unmarshal …An option has the wrong type for native (e.g. a priority weight above 255).
HTTP/3 request failedQUIC 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 204

Native writes this to stdout, outside Node's console.

Common problems

  • The server sees User-Agent: tls-client/2.2.0 or Go-http-client/2.0. No user-agent header 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-Agent and profile is the most common cause.
  • initTLS() fails on Alpine. musl isn't supported yet. See Installation.
  • Every request is status: 0 with racing on. withProtocolRacing needs a SOCKS5 proxy or none. See HTTP/3.
  • Requests hang at high volume. The threadpool is saturated. See Concurrency.

On this page