diff --git a/lib/core/util.js b/lib/core/util.js
index 7cd411d9f3e..94a8a04c89e 100644
--- a/lib/core/util.js
+++ b/lib/core/util.js
@@ -349,14 +349,8 @@ function validateHandler (handler, method, upgrade) {
 // A body is disturbed if it has been read from and it cannot
 // be re-used without losing state or data.
 function isDisturbed (body) {
-  return !!(body && (
-    stream.isDisturbed
-      ? stream.isDisturbed(body) || body[kBodyUsed] // TODO (fix): Why is body[kBodyUsed] needed?
-      : body[kBodyUsed] ||
-        body.readableDidRead ||
-        (body._readableState && body._readableState.dataEmitted) ||
-        isReadableAborted(body)
-  ))
+  // TODO (fix): Why is body[kBodyUsed] needed?
+  return !!(body && (stream.isDisturbed(body) || body[kBodyUsed]))
 }
 
 function isErrored (body) {
diff --git a/lib/fetch/index.js b/lib/fetch/index.js
index f16ce4b4536..75f25371410 100644
--- a/lib/fetch/index.js
+++ b/lib/fetch/index.js
@@ -327,13 +327,6 @@ function markResourceTiming (timingInfo, originalURL, initiatorType, globalThis,
 
 // https://fetch.spec.whatwg.org/#abort-fetch
 function abortFetch (p, request, responseObject, error) {
-  // Note: AbortSignal.reason was added in node v17.2.0
-  // which would give us an undefined error to reject with.
-  // Remove this once node v16 is no longer supported.
-  if (!error) {
-    error = new DOMException('The operation was aborted.', 'AbortError')
-  }
-
   // 1. Reject promise with error.
   p.reject(error)
 
diff --git a/test/client-node-max-header-size.js b/test/client-node-max-header-size.js
index b5374901644..b4bd6280249 100644
--- a/test/client-node-max-header-size.js
+++ b/test/client-node-max-header-size.js
@@ -7,8 +7,7 @@ const command = 'node -e "require(\'.\').request(\'https://httpbin.org/get\')"'
 
 test("respect Node.js' --max-http-header-size", async (t) => {
   t.throws(
-    // TODO: Drop the `--unhandled-rejections=throw` once we drop Node.js 14
-    () => execSync(`${command} --max-http-header-size=1 --unhandled-rejections=throw`),
+    () => execSync(`${command} --max-http-header-size=1`),
     /UND_ERR_HEADERS_OVERFLOW/,
     'max-http-header-size=1 should throw'
   )
diff --git a/test/fetch/client-node-max-header-size.js b/test/fetch/client-node-max-header-size.js
index 260e11ec378..d044731cf95 100644
--- a/test/fetch/client-node-max-header-size.js
+++ b/test/fetch/client-node-max-header-size.js
@@ -8,8 +8,7 @@ const command = 'node -e "require(\'./undici-fetch.js\').fetch(\'https://httpbin
 
 test("respect Node.js' --max-http-header-size", async () => {
   assert.throws(
-    // TODO: Drop the `--unhandled-rejections=throw` once we drop Node.js 14
-    () => execSync(`${command} --max-http-header-size=1 --unhandled-rejections=throw`),
+    () => execSync(`${command} --max-http-header-size=1`),
     /UND_ERR_HEADERS_OVERFLOW/,
     'max-http-header-size=1 should throw'
   )
diff --git a/test/fetch/response.js b/test/fetch/response.js
index 94b1e8a8199..c22a649ff28 100644
--- a/test/fetch/response.js
+++ b/test/fetch/response.js
@@ -165,7 +165,7 @@ test('Modifying headers using Headers.prototype.set', () => {
 })
 
 // https://github.com/nodejs/node/issues/43838
-test('constructing a Response with a ReadableStream body', { skip: process.version.startsWith('v16.') }, async (t) => {
+test('constructing a Response with a ReadableStream body', async (t) => {
   const text = '{"foo":"bar"}'
   const uint8 = new TextEncoder().encode(text)
 
@@ -199,7 +199,7 @@ test('constructing a Response with a ReadableStream body', { skip: process.versi
     await assert.rejects(response.text(), TypeError)
   })
 
-  await t.test('Readable with ArrayBuffer chunk still throws', { skip: process.version.startsWith('v16.') }, async () => {
+  await t.test('Readable with ArrayBuffer chunk still throws', async () => {
     const readable = new ReadableStream({
       start (controller) {
         controller.enqueue(uint8.buffer)
@@ -210,14 +210,12 @@ test('constructing a Response with a ReadableStream body', { skip: process.versi
     const response1 = new Response(readable)
     const response2 = response1.clone()
     const response3 = response1.clone()
-    // const response4 = response1.clone()
+    const response4 = response1.clone()
 
     await assert.rejects(response1.arrayBuffer(), TypeError)
     await assert.rejects(response2.text(), TypeError)
     await assert.rejects(response3.json(), TypeError)
-    // TODO: on Node v16.8.0, this throws a TypeError
-    // because the body is detected as disturbed.
-    // await t.rejects(response4.blob(), TypeError)
+    await assert.rejects(response4.blob(), TypeError)
   })
 })
 
diff --git a/types/dispatcher.d.ts b/types/dispatcher.d.ts
index 5988c8a9c7d..33258aafe49 100644
--- a/types/dispatcher.d.ts
+++ b/types/dispatcher.d.ts
@@ -229,7 +229,7 @@ declare namespace Dispatcher {
    * @link https://fetch.spec.whatwg.org/#body-mixin
    */
   interface BodyMixin {
-    readonly body?: never; // throws on node v16.6.0
+    readonly body?: never;
     readonly bodyUsed: boolean;
     arrayBuffer(): Promise<ArrayBuffer>;
     blob(): Promise<Blob>;
diff --git a/types/readable.d.ts b/types/readable.d.ts
index 4549a8c87e8..a5fce8a20d3 100644
--- a/types/readable.d.ts
+++ b/types/readable.d.ts
@@ -44,9 +44,8 @@ declare class BodyReadable extends Readable {
    */
   readonly bodyUsed: boolean
 
-  /** Throws on node 16.6.0
-   *
-   *  If body is null, it should return null as the body
+  /** 
+   * If body is null, it should return null as the body
    *
    *  If body is not null, should return the body as a ReadableStream
    *