denoland/deno · error · TypeError

Cannot read url: request closed

Error message

Cannot read url: request closed

What it means

InnerRequest.currentUrl() resolves the LAST entry of urlList (the URL after redirects) lazily via the urlList[currentIndex]() closure. If that underlying source is closed, the call throws and is converted to TypeError 'Cannot read url: request closed' (capital C, matching currentUrl()). Distinct from error 84 only in which accessor and list index is involved.

Source

Thrown at ext/fetch/23_request.js:177

    clientRid: null,
    blobUrlEntry,
    url() {
      if (this.urlListProcessed[0] === undefined) {
        try {
          this.urlListProcessed[0] = this.urlList[0]();
        } catch {
          throw new TypeError("cannot read url: request closed");
        }
      }
      return this.urlListProcessed[0];
    },
    currentUrl() {
      const currentIndex = this.urlList.length - 1;
      if (this.urlListProcessed[currentIndex] === undefined) {
        try {
          this.urlListProcessed[currentIndex] = this.urlList[currentIndex]();
        } catch {
          throw new TypeError("Cannot read url: request closed");
        }
      }
      return this.urlListProcessed[currentIndex];
    },
  };
}

/**
 * https://fetch.spec.whatwg.org/#concept-request-clone
 * @param {InnerRequest} request
 * @param {boolean} skipBody
 * @returns {InnerRequest}
 */
function cloneInnerRequest(request, skipBody = false) {
  const headerList = ArrayPrototypeMap(
    request.headerList,
    (x) => [x[0], x[1]],
  );

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Materialize the current/final URL while the request resource is still open (right after fetch resolves)
  2. Snapshot response.url and other derived strings instead of re-deriving them later from the request
  3. Tie request processing to request.signal so work stops when the connection closes

Example fix

// before
const job = fetch('https://example.com/a') // redirects
  .then((res) => ({ get url() { return res.url; /* late access may hit closed source */ } }));

// after
const job = fetch('https://example.com/a').then((res) => {
  const url = res.url; // capture eagerly
  return { url };
});
Defensive patterns

Strategy: try-catch

Validate before calling

const finalUrl = (() => {
  try {
    return response.url; // materialize final (post-redirect) URL now
  } catch {
    return null;
  }
})();

Try / catch

try {
  report(req.currentUrl()); // or response.url
} catch (err) {
  if (err instanceof TypeError && err.message === 'Cannot read url: request closed') return; // source gone, skip
  throw err;
}

Prevention

When it happens

Trigger: Calling request.currentUrl() (used internally after redirect chains) on an op-backed request whose resource closed before the currentUrl getter could cache urlListProcessed[currentIndex].

Common situations: Redirect handling or response URL reporting that runs after the originating connection/resource was reaped; deferred logging of final URLs for followed redirects.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/c12435333278e771. Report an issue: GitHub.