overleaf/overleaf · warning · BadRequestError

requested version past the end of the history

Error message

requested version past the end of the history

What it means

getChangesInChunkSince throws Errors.BadRequestError when sinceVersion is greater than the latest chunk's end version, meaning the caller asked for changes since a version that is beyond the end of the project's recorded history. It protects against invalid version queries.

Source

Thrown at services/project-history/app/js/SnapshotManager.js:238

  const changes = chunk.getChanges()
  snapshot.applyAll(changes)
  return {
    snapshot,
    version: chunk.getEndVersion(),
  }
}

async function getChangesInChunkSince(projectId, historyId, sinceVersion) {
  const latestChunk = Core.Chunk.fromRaw(
    (
      await HistoryStoreManager.promises.getMostRecentChunk(
        projectId,
        historyId
      )
    ).chunk
  )
  if (sinceVersion > latestChunk.getEndVersion()) {
    throw new Errors.BadRequestError(
      'requested version past the end of the history'
    )
  }
  const latestStartVersion = latestChunk.getStartVersion()
  let chunk = latestChunk
  if (sinceVersion < latestStartVersion) {
    chunk = Core.Chunk.fromRaw(
      (
        await HistoryStoreManager.promises.getChunkAtVersion(
          projectId,
          historyId,
          sinceVersion
        )
      ).chunk
    )
  }
  const changes = chunk
    .getChanges()

View on GitHub (pinned to 28ad3b03b7)

Solutions

  1. Clamp/validate sinceVersion against the history's end version before calling.
  2. After a hard resync, clients must re-fetch the current version instead of reusing old ones.
  3. Return 400 to the caller and have it reset its local version tracking.
  4. Verify the correct historyId is being used for the project.

Example fix

// before
const changes = await SnapshotManager.promises.getChangesInChunkSince(projectId, historyId, sinceVersion)
// after
const latestChunk = (await ChunkManager.promises.getLatestChunk(projectId, historyId)).chunk
if (sinceVersion > latestChunk.getEndVersion()) {
  sinceVersion = latestChunk.getEndVersion() // or reset client state
}
const changes = await SnapshotManager.promises.getChangesInChunkSince(projectId, historyId, sinceVersion)
Defensive patterns

Strategy: validation

Validate before calling

const latestChunk = (await ChunkManager.promises.getLatestChunk(projectId, historyId)).chunk
if (sinceVersion > latestChunk.getEndVersion()) {
  sinceVersion = latestChunk.getEndVersion()
}

Try / catch

try {
  await SnapshotManager.promises.getChangesInChunkSince(projectId, historyId, sinceVersion)
} catch (err) {
  if (err instanceof Errors.BadRequestError && err.message.includes('past the end')) {
    // reset client version tracking and re-sync from latest
  } else {
    throw err
  }
}

Prevention

When it happens

Trigger: Calling getChangesInChunkSince(projectId, historyId, sinceVersion) where sinceVersion > latestChunk.getEndVersion() — e.g. a stale client sends a version from a different/forked history or an out-of-range number.

Common situations: Clients caching a version from before a history reset or resync; hard resync rewrote history so old versions no longer exist; off-by-one or wrong project's version sent to another project's history; replaying updates after history truncation.

Related errors


AI-assisted analysis of overleaf/overleaf@28ad3b03b7 (2026-09-03). Data as JSON: /api/errors/2b85331e82701ac1. Report an issue: GitHub.