{"record":{"id":"f39853b5c8f2cd25","repo":"overleaf/overleaf","slug":"ranges-are-too-large","errorCode":null,"errorMessage":"ranges are too large","messagePattern":"ranges are too large","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"services/document-updater/app/js/RedisManager.js","lineNumber":616,"sourceCode":"      // Too late to lock the project\n      await rclient.del(keys.projectBlock({ project_id: projectId }))\n      return false\n    }\n    return true\n  },\n\n  async unblockProject(projectId) {\n    const reply = await rclient.del(\n      keys.projectBlock({ project_id: projectId })\n    )\n    const wasBlocked = reply === 1\n    return wasBlocked\n  },\n\n  _serializeRanges(ranges) {\n    let jsonRanges = JSON.stringify(ranges)\n    if (jsonRanges && jsonRanges.length > MAX_RANGES_SIZE) {\n      throw new Error('ranges are too large')\n    }\n    if (jsonRanges === '{}') {\n      // Most doc will have empty ranges so don't fill redis with lots of '{}' keys\n      jsonRanges = null\n    }\n    return jsonRanges\n  },\n\n  _deserializeRanges(ranges) {\n    if (ranges == null || ranges === '') {\n      return {}\n    } else {\n      return JSON.parse(ranges)\n    }\n  },\n\n  _computeHash(docLines) {\n    // use sha1 checksum of doclines to detect data corruption.","sourceCodeStart":598,"sourceCodeEnd":634,"githubUrl":"https://github.com/overleaf/overleaf/blob/28ad3b03b71cb4311decdcb55c36b33ec10d72db/services/document-updater/app/js/RedisManager.js#L598-L634","documentation":"_serializeRanges JSON-stringifies the ranges object and throws a plain Error when the serialized form exceeds MAX_RANGES_SIZE. This prevents unboundedly large track-changes/comment range payloads from being stored in Redis and slowing down every subsequent doc load.","triggerScenarios":"Calling updateDocument (which calls _serializeRanges) or _serializeRanges directly with a ranges object whose JSON representation is larger than MAX_RANGES_SIZE bytes.","commonSituations":"Very long-lived docs accumulating thousands of track-changes entries; comments never resolved/accepted over years of editing; an overly small MAX_RANGES_SIZE configured for a heavy-collaboration workflow.","solutions":["Accept/reject and delete old track-changes to shrink the ranges payload","Resolve or remove stale comments in the document","Raise MAX_RANGES_SIZE in RedisManager if legitimate workloads need more room","Periodically compact/prune ranges via the track-changes service","Archive very old documents and start fresh copies"],"exampleFix":"// before: letting ranges grow unbounded\nawait applyRangesUpdate(projectId, docId, incomingRanges)\n// after: pre-trim ranges before pushing\nfunction pruneRanges(ranges, maxSize = 1000) {\n  const trimmed = {\n    comments: (ranges.comments || []).slice(-maxSize),\n    changes: (ranges.changes || []).slice(-maxSize),\n  }\n  if (JSON.stringify(trimmed).length > MAX_RANGES_SIZE) throw new Error('ranges too large after pruning')\n  return trimmed\n}\nawait applyRangesUpdate(projectId, docId, pruneRanges(ranges))","handlingStrategy":"validation","validationCode":"const jsonRanges = JSON.stringify(ranges)\nif (jsonRanges && jsonRanges.length > MAX_RANGES_SIZE) {\n  throw new Error(`ranges payload too large: ${jsonRanges.length} > ${MAX_RANGES_SIZE}`)\n}","typeGuard":null,"tryCatchPattern":"try {\n  const jsonRanges = RedisManager._serializeRanges(ranges)\n  // proceed\n} catch (err) {\n  if (err.message === 'ranges are too large') {\n    await pruneStaleRanges(projectId, docId) // accept/delete old track-changes, then retry\n  } else {\n    throw err\n  }\n}","preventionTips":["Periodically accept/reject track-changes and resolve comments","Monitor serialized ranges size per doc and alert near the limit","Size MAX_RANGES_SIZE to your heaviest legitimate documents","Archive long-lived docs to keep range history compact"],"tags":["ranges","size-limit","track-changes","redis"],"backgroundTag":"payload-size-limit-exceeded","analyzedSha":"28ad3b03b71cb4311decdcb55c36b33ec10d72db","analyzedAt":"2026-09-03T02:10:22.807Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T07:17:11.731Z"}