denoland/deno · error · NodeRangeError

ERR_OUT_OF_RANGE

ERR_OUT_OF_RANGE

Error message

The value of "start" is out of range. It must be <= "end" (here: ${this.end}). Received ${actual}

What it means

In the ReadStream constructor, start is validated as an integer >= 0 and, whenever end is not Infinity, start must not exceed end. A byte window whose first byte lies past its last byte cannot be read, so construction fails with ERR_OUT_OF_RANGE reporting the actual end value.

Source

Thrown at ext/node/polyfills/internal/fs/streams.mjs:254

  }

  this.start = options.start;
  this.end = options.end ?? NumberPOSITIVE_INFINITY;
  this.pos = undefined;
  this.bytesRead = 0;
  this[kIsPerformingIO] = false;

  if (this.start !== undefined) {
    validateInteger(this.start, "start", 0);

    this.pos = this.start;
  }

  if (this.end !== NumberPOSITIVE_INFINITY) {
    validateInteger(this.end, "end", 0);

    if (this.start !== undefined && this.start > this.end) {
      throw new ERR_OUT_OF_RANGE(
        "start",
        `<= "end" (here: ${this.end})`,
        this.start,
      );
    }
  }

  ReflectApply(lazyStream().Readable, this, [options]);
}

ObjectSetPrototypeOf(ReadStream.prototype, lazyStream().Readable.prototype);
ObjectSetPrototypeOf(ReadStream, lazyStream().Readable);

ObjectDefineProperty(ReadStream.prototype, "autoClose", {
  __proto__: null,
  get() {
    return this._readableState.autoDestroy;
  },

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Fix the range so start <= end — usually the two bounds are swapped.
  2. Omit end when reading to EOF; it defaults to Infinity and the comparison is skipped.
  3. Clamp defensively before constructing: start = Math.min(start, end).

Example fix

// before
const s = fs.createReadStream(f, { start: 100, end: 50 }); // throws

// after
const s = fs.createReadStream(f, { start: 50, end: 100 });
// or read to EOF from an offset
const s = fs.createReadStream(f, { start: 50 });
Defensive patterns

Strategy: validation

Validate before calling

function validRange(o = {}) {
  const end = o.end ?? Infinity;
  if (o.start === undefined) return true;
  return Number.isInteger(o.start) && o.start >= 0 && o.start <= end;
}
if (!validRange(opts)) throw new RangeError('start must be an integer >= 0 and <= end');

Prevention

When it happens

Trigger: fs.createReadStream(f, { start: 100, end: 50 }); range math that swaps the bounds or derives end from a shorter content-length; { start: -1 } also fails earlier via validateInteger.

Common situations: HTTP Range-request handlers translating headers into stream options; download resume logic where start equals the new file size but end was computed from the old size; reversed pagination indexes from a UI feeding a file slicer.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/3ba984b83cc9f7db. Report an issue: GitHub.