mozilla/pdf.js · error · Error

Bad end offset: ${end}

Error message

Bad end offset: ${end}

What it means

Thrown by ChunkedStream.onReceiveData when a delivered chunk's end position (begin + chunk.byteLength) is neither aligned to chunkSize nor exactly equal to the document's total byte length. The library enforces this so its internal _loadedChunks set stays consistent with the chunk grid. A violation means the byte-range transport and the configured chunkSize disagree about chunk boundaries.

Source

Thrown at src/core/chunked_stream.js:71

  get numChunksLoaded() {
    return this._loadedChunks.size;
  }

  get isDataLoaded() {
    return this.numChunksLoaded === this.numChunks;
  }

  onReceiveData(begin, chunk) {
    const chunkSize = this.chunkSize;
    if (begin % chunkSize !== 0) {
      throw new Error(`Bad begin offset: ${begin}`);
    }

    // Using `this.length` is inaccurate here since `this.start` can be moved
    // (see the `moveStart` method).
    const end = begin + chunk.byteLength;
    if (end % chunkSize !== 0 && end !== this.bytes.length) {
      throw new Error(`Bad end offset: ${end}`);
    }

    if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
      assert(
        chunk instanceof ArrayBuffer,
        "onReceiveData - expected an ArrayBuffer."
      );
    }
    this.bytes.set(new Uint8Array(chunk), begin);
    const beginChunk = Math.floor(begin / chunkSize);
    const endChunk = Math.floor((end - 1) / chunkSize) + 1;

    for (let curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
      // Since a value can only occur *once* in a `Set`, there's no need to
      // manually check `Set.prototype.has()` before adding the value here.
      this._loadedChunks.add(curChunk);
    }
  }

View on GitHub (pinned to 5903d58d58)

Solutions

  1. Align server Range responses to the rangeChunkSize value passed to getDocument so every served chunk starts and ends on a chunkSize boundary.
  2. Verify Content-Range and Content-Length headers agree with the byte range the client requested.
  3. If you implement a custom IPDFFileStreamReader/NetworkLayer, return chunks whose begin and end are multiples of chunkSize (the final chunk excepted).
  4. Disable range requests via getDocument({ disableRange: true }) when the server cannot honor aligned ranges.

Example fix

// before
stream.onReceiveData(begin, chunk); // begin=5000, chunkSize=65536

// after
const cs = stream.chunkSize;
const end = begin + chunk.byteLength;
if (begin % cs === 0 && (end % cs === 0 || end === stream.bytes.length)) {
  stream.onReceiveData(begin, chunk);
} else {
  console.warn('Ignoring mis-aligned chunk', { begin, end, cs });
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate chunk alignment before invoking onReceiveData
function safeReceiveData(stream, begin, chunk) {
  const cs = stream.chunkSize;
  const end = begin + chunk.byteLength;
  if (begin % cs !== 0) {
    console.warn('begin not aligned', { begin, cs });
    return false;
  }
  if (end % cs !== 0 && end !== stream.bytes.length) {
    console.warn('end not aligned', { end, cs, total: stream.bytes.length });
    return false;
  }
  stream.onReceiveData(begin, chunk);
  return true;
}

Prevention

When it happens

Trigger: Calling onReceiveData(begin, chunk) where (begin + chunk.byteLength) % chunkSize !== 0 and the end is not the final byte. Occurs when a Range-request server returns responses whose sizes differ from the client's rangeChunkSize, when Content-Range/Content-Length disagree, or when a custom network/stream-reader layer passes mis-sized chunks.

Common situations: Self-hosted PDF behind a CDN or reverse proxy that re-chunks responses; mismatched rangeChunkSize between client and origin server; multi-part/byteranges responses served in arbitrary pieces; a server that ignores Range headers and streams the whole file split arbitrarily.

Related errors


AI-assisted analysis of mozilla/pdf.js@5903d58d58 (2026-08-13). Data as JSON: /api/errors/f59c889a50c49a00. Report an issue: GitHub.