{"record":{"id":"c5291ad559e52724","repo":"denoland/deno","slug":"err-http2-no-socket-manipulation","errorCode":"ERR_HTTP2_NO_SOCKET_MANIPULATION","errorMessage":"HTTP/2 sockets should not be directly manipulated (e.g. read and written)","messagePattern":"HTTP/2 sockets should not be directly manipulated \\(e\\.g\\. read and written\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/http2.ts","lineNumber":807,"sourceCode":"\nconst proxySocketHandler = {\n  get(session, prop) {\n    switch (prop) {\n      case \"setTimeout\":\n      case \"ref\":\n      case \"unref\":\n        return FunctionPrototypeBind(session[prop], session);\n      case \"destroy\":\n      case \"emit\":\n      case \"end\":\n      case \"pause\":\n      case \"read\":\n      case \"resume\":\n      case \"write\":\n      case \"setEncoding\":\n      case \"setKeepAlive\":\n      case \"setNoDelay\":\n        throw new ERR_HTTP2_NO_SOCKET_MANIPULATION();\n      default: {\n        const socket = session[kSocket];\n        if (socket === undefined) {\n          throw new ERR_HTTP2_SOCKET_UNBOUND();\n        }\n        const value = socket[prop];\n        return typeof value === \"function\"\n          ? FunctionPrototypeBind(value, socket)\n          : value;\n      }\n    }\n  },\n  getPrototypeOf(session) {\n    const socket = session[kSocket];\n    if (socket === undefined) {\n      throw new ERR_HTTP2_SOCKET_UNBOUND();\n    }\n    return ReflectGetPrototypeOf(socket);","sourceCodeStart":789,"sourceCodeEnd":825,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/http2.ts#L789-L825","documentation":"Http2Session exposes its socket through a proxy whose get trap throws ERR_HTTP2_NO_SOCKET_MANIPULATION for destroy, emit, end, pause, read, resume, write, setEncoding, setKeepAlive, and setNoDelay. HTTP/2 multiplexes many streams over a single connection, so direct reads/writes or lifecycle calls on the raw socket would corrupt the binary framing for every stream; only session-level APIs are legal.","triggerScenarios":"session.socket.write('x'); session.socket.destroy(); session.socket.pause() / resume(); session.socket.read(); session.socket.setEncoding('utf8'); session.socket.setNoDelay(true); session.socket.emit('drain'). Accessing these through the proxy (get trap, then invoking) triggers the throw.","commonSituations":"Middleware written for HTTP/1 that tunes sockets (setNoDelay, setKeepAlive) applied to an http2 server; code sniffing or writing raw bytes for protocol detection; test harnesses that pause/resume the socket to simulate backpressure; libraries that call socket.destroy() for cleanup on shutdown.","solutions":["Use the session-level equivalents: session.destroy(), session.setTimeout(), session.close() instead of socket methods","Branch on protocol before touching the socket: if (req.httpVersion === '2.0' || session.constructor.name === 'Http2Session') skip socket manipulation","Operate on the individual Http2Stream (readable/writable sides) for data flow instead of the socket"],"exampleFix":"// before\nsession.socket.setNoDelay(true); // throws ERR_HTTP2_NO_SOCKET_MANIPULATION\nsession.socket.destroy();\n\n// after\nsession.setTimeout(5000); // allowed through the proxy\nsession.destroy(); // session-level lifecycle API","handlingStrategy":"type-guard","validationCode":"const isHttp2 = (s: { constructor: { name: string } }) =>\n  s.constructor.name === 'Http2Session';\nif (!isHttp2(session)) session.socket.setNoDelay(true); // http1 only","typeGuard":"import type { Http2Session } from 'node:http2';\nimport type { Socket } from 'node:net';\nconst isHttp2Session = (s: Http2Session | { socket: Socket }): s is Http2Session =>\n  (s as Http2Session).alpnProtocol !== undefined &&\n  typeof (s as Http2Session).goaway === 'number';","tryCatchPattern":"try {\n  session.socket.write(chunk);\n} catch (err) {\n  if ((err as NodeJS.ErrnoException).code === 'ERR_HTTP2_NO_SOCKET_MANIPULATION') {\n    // route the data through the Http2Stream / session APIs instead\n  } else throw err;\n}","preventionTips":["Never read/write the raw socket on http2 connections; use stream and session APIs","Gate socket-tuning middleware on req.httpVersion !== '2'","Only setTimeout/ref/unref are proxied through — design cleanup around session.destroy()"],"tags":["http2","socket","node-compat","protocol-misuse"],"backgroundTag":"http2-socket-manipulation","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}