denoland/deno · error

ERR_INDEX_OUT_OF_RANGE

ERR_INDEX_OUT_OF_RANGE

Error message

Index out of range

What it means

Buffer hex-encoding (toString('hex', start, end)) validates slice bounds: negative start or end values are out of range because slice indices must be non-negative byte offsets. hexIndexOutOfRange() throws ERR_INDEX_OUT_OF_RANGE to match Node's behavior rather than silently clamping.

Source

Thrown at ext/node/polyfills/internal/buffer.mjs:1269

function hexIndexOutOfRange() {
  const err = new RangeError("Index out of range");
  err.code = "ERR_OUT_OF_RANGE";
  return err;
}

Buffer.prototype.hexSlice = function hexSlice(start, end) {
  let byteLength = TypedArrayPrototypeGetByteLength(this);
  // Index semantics replicate Node's C++ StringSlice: zero-length receivers
  // (including detached) return "" before any validation, ToInteger
  // coercion, negative index throws "Index out of range", end < start
  // clamps to empty, and only a forward range with end > length throws.
  if (byteLength === 0) {
    return "";
  }
  start = start === undefined ? 0 : MathTrunc(Number(start)) || 0;
  end = end === undefined ? byteLength : MathTrunc(Number(end)) || 0;
  if (start < 0 || end < 0) {
    throw hexIndexOutOfRange();
  }
  if (end <= start) {
    return "";
  }
  // Re-read: argument coercion can run user code that resizes or detaches
  // the underlying buffer (detached views report length 0).
  byteLength = TypedArrayPrototypeGetByteLength(this);
  if (end > byteLength) {
    throw hexIndexOutOfRange();
  }
  if (end - start > kStringMaxLength / 2) {
    throw genericNodeError(
      `Cannot create a string longer than 0x${
        NumberPrototypeToString(kStringMaxLength, 16)
      } characters`,
      { code: "ERR_STRING_TOO_LONG" },
    );
  }

View on GitHub (pinned to a961cdec3b)

Solutions

  1. Clamp indices before the call: start = Math.max(0, start); end = Math.max(0, end).
  2. Fix the index arithmetic so start/end are non-negative byte offsets within [0, byteLength].
  3. Coerce with MathTrunc and validate user-supplied indices before use.

Example fix

// before
const hex = buf.toString('hex', 0, buf.length - 8); // negative when short
// after
const end = Math.max(0, buf.byteLength - 8);
const hex = buf.toString('hex', 0, end);
Defensive patterns

Strategy: validation

Validate before calling

function clampHexRange(buf, start = 0, end = buf.byteLength) {
  start = Math.max(0, Math.trunc(start) || 0);
  end = Math.max(0, Math.trunc(end) || 0);
  return [start, Math.min(end, buf.byteLength)];
}
const [s, e] = clampHexRange(buf, userStart, userEnd);
const hex = buf.toString('hex', s, e);

Type guard

function isSafeIndex(v) {
  return Number.isInteger(v) && v >= 0;
}

Try / catch

try {
  hex = buf.toString('hex', start, end);
} catch (err) {
  if (err.code === 'ERR_INDEX_OUT_OF_RANGE') {
    [start, end] = clampHexRange(buf, start, end);
    hex = buf.toString('hex', start, end);
  }
}

Prevention

When it happens

Trigger: Calling buf.toString('hex', start, end) with a negative start or a negative end — e.g. end computed as buf.length - extra where extra > length, or -1 passed as an end sentinel.

Common situations: Off-by-one arithmetic producing negative end; porting code that used negative indices like Python/JS array slicing; uninitialized length variables.

Related errors


AI-assisted analysis of denoland/deno@a961cdec3b (2026-09-03). Data as JSON: /api/errors/b67dace5be4a0abe. Report an issue: GitHub.