Some — not all — of the backends behind api.telegram.org abort the TLS handshake with a TCP RST when the ClientHello offers the post-quantum hybrid group X25519MLKEM768.
That group is enabled by default in OpenSSL 3.5.x, which is what Node.js 24 bundles. So any Node 24 bot hits this without configuring anything, and will keep hitting it as more runtimes pick up OpenSSL 3.5 / PQ-by-default.
The failure surfaces to the application as a generic network error on sendMessage — indistinguishable from ordinary flakiness — so bots retry blindly or drop the message. It cost us a silently lost reply before we found it.
Measurement
Interleaved A/B (alternating one connection per arm, so a transient outage can't be attributed to one side). 125 fresh TLS connections per arm to api.telegram.org:443, resolving to 149.154.166.110:
| ClientHello groups |
ECONNRESET |
OpenSSL 3.5.7 default (includes X25519MLKEM768) |
21 / 125 (16.8%) |
X25519:prime256v1 (classical only) |
0 / 125 (0.0%) |
Negative controls, same process, same interleaving, 25 connections per arm: api.anthropic.com, api.openai.com, www.googleapis.com, oauth2.googleapis.com, graph.microsoft.com — 0 resets on both arms. So this is specific to api.telegram.org, not to the client, the network, or the PQ group as such.
Ruling out the local network: curl from inside the same container (system OpenSSL 3.0.20, which has no MLKEM support and so never offers it) 0/70; from the Docker host 20/20; from a laptop on a different network 20/20. Only the process offering the PQ group fails.
Likely cause
An MLKEM768 key share is ~1.2 KB, which pushes the ClientHello past a single TCP segment. This looks like a load balancer or TLS terminator in part of the pool that can't reassemble a multi-segment ClientHello. The inconsistency across connections to the same IP suggests it is per-backend rather than per-edge, which would also explain why it reads as intermittent flakiness rather than a hard failure.
Reproducer
Node 24, no dependencies. Expect a nonzero count on the first arm and zero on the second:
const tls = require("node:tls");
const N = 125;
const connect = (curve) => new Promise((res) => {
const opts = { host: "api.telegram.org", port: 443, servername: "api.telegram.org" };
if (curve) opts.ecdhCurve = curve;
const s = tls.connect(opts, () => { s.destroy(); res(null); });
s.on("error", (e) => res(e.code));
});
(async () => {
let a = 0, b = 0;
for (let i = 0; i < N; i++) { // interleaved, not batched
if (await connect(undefined) === "ECONNRESET") a++;
if (await connect("X25519:prime256v1") === "ECONNRESET") b++;
}
console.log({ defaultGroups: a, classicalOnly: b, of: N });
})();
Environment
- Node
v24.18.0, OpenSSL 3.5.7 (Node's bundled build)
- Debian 12 bookworm, linux/x64, in Docker
- Observed continuously; the A/B above is from 2026-09-01
Client-side workaround
For anyone else landing here: set tls.DEFAULT_ECDH_CURVE = "X25519:prime256v1" before any TLS connection is opened. Two gotchas — it is per-JS-context, so worker threads need it set again (NODE_OPTIONS reaches child processes but is not in process.execArgv, so it never reaches worker threads); and setting Groups via OPENSSL_CONF does not work, because Node ignores it.
I appreciate this repo is the self-hosted Bot API server rather than the hosted infrastructure — filing here because it's the closest public tracker for api.telegram.org behaviour. Happy to move it if there's a better venue.
Some — not all — of the backends behind
api.telegram.orgabort the TLS handshake with a TCP RST when the ClientHello offers the post-quantum hybrid groupX25519MLKEM768.That group is enabled by default in OpenSSL 3.5.x, which is what Node.js 24 bundles. So any Node 24 bot hits this without configuring anything, and will keep hitting it as more runtimes pick up OpenSSL 3.5 / PQ-by-default.
The failure surfaces to the application as a generic network error on
sendMessage— indistinguishable from ordinary flakiness — so bots retry blindly or drop the message. It cost us a silently lost reply before we found it.Measurement
Interleaved A/B (alternating one connection per arm, so a transient outage can't be attributed to one side). 125 fresh TLS connections per arm to
api.telegram.org:443, resolving to149.154.166.110:X25519MLKEM768)X25519:prime256v1(classical only)Negative controls, same process, same interleaving, 25 connections per arm:
api.anthropic.com,api.openai.com,www.googleapis.com,oauth2.googleapis.com,graph.microsoft.com— 0 resets on both arms. So this is specific toapi.telegram.org, not to the client, the network, or the PQ group as such.Ruling out the local network:
curlfrom inside the same container (system OpenSSL 3.0.20, which has no MLKEM support and so never offers it) 0/70; from the Docker host 20/20; from a laptop on a different network 20/20. Only the process offering the PQ group fails.Likely cause
An MLKEM768 key share is ~1.2 KB, which pushes the ClientHello past a single TCP segment. This looks like a load balancer or TLS terminator in part of the pool that can't reassemble a multi-segment ClientHello. The inconsistency across connections to the same IP suggests it is per-backend rather than per-edge, which would also explain why it reads as intermittent flakiness rather than a hard failure.
Reproducer
Node 24, no dependencies. Expect a nonzero count on the first arm and zero on the second:
Environment
v24.18.0, OpenSSL3.5.7(Node's bundled build)Client-side workaround
For anyone else landing here: set
tls.DEFAULT_ECDH_CURVE = "X25519:prime256v1"before any TLS connection is opened. Two gotchas — it is per-JS-context, so worker threads need it set again (NODE_OPTIONSreaches child processes but is not inprocess.execArgv, so it never reaches worker threads); and settingGroupsviaOPENSSL_CONFdoes not work, because Node ignores it.I appreciate this repo is the self-hosted Bot API server rather than the hosted infrastructure — filing here because it's the closest public tracker for
api.telegram.orgbehaviour. Happy to move it if there's a better venue.