overleaf/overleaf · error · OError

unrecognised file: not in snapshot

Error message

unrecognised file: not in snapshot

What it means

expandResyncDocContentUpdate resolves the doc's pathname via UpdateTranslator._convertPathname and looks it up in this.files (the snapshot's file map). If no entry exists, the resync expects content for a file the snapshot does not know about, so it throws OError('unrecognised file: not in snapshot') — the snapshot and the resync request are inconsistent.

Source

Thrown at services/project-history/app/js/SyncManager.js:895

      Metrics.inc('project_history_resync_operation', 1, {
        status: 'update binary file contents',
      })
    }
  }

  /**
   * Expand a resyncDocContentUpdate
   *
   * @param {ResyncDocContentUpdate} update
   */
  async expandResyncDocContentUpdate(update) {
    const pathname = UpdateTranslator._convertPathname(update.path)
    const snapshotFile = this.files[pathname]
    const expectedFile = update.resyncDocContent
    const expectedContent = expectedFile.content

    if (!snapshotFile) {
      throw new OError('unrecognised file: not in snapshot')
    }

    // Compare hashes to see if the persisted file matches the expected content.
    // The hash of the persisted files is stored in the snapshot.
    // Note getHash() returns the hash only when the persisted file has
    // no changes in the snapshot, the hash is null if there are changes
    // that apply to it.
    let hashesMatch = false
    const persistedHash = snapshotFile.getHash()
    if (persistedHash != null) {
      const expectedHash = HashManager._getBlobHashFromString(expectedContent)
      if (persistedHash === expectedHash) {
        logger.debug(
          { projectId: this.projectId, persistedHash, expectedHash },
          'skipping diff because hashes match and persisted file has no ops'
        )
        hashesMatch = true
      }

View on GitHub (pinned to 28ad3b03b7)

Solutions

  1. Reload the snapshot for the current historyId so it includes the file
  2. Skip doc-content resync for deleted files, or re-run a full project-structure resync to realign snapshot and expectations
  3. Verify path conversion matches how the snapshot keys files (UpdateTranslator._convertPathname vs raw path)
  4. Ensure resync start and update processing use the same project version/history snapshot

Example fix

// before
const snapshotFile = this.files[pathname]
if (!snapshotFile) throw new OError('unrecognised file: not in snapshot')
// after: tolerate files removed since resync started
const snapshotFile = this.files[pathname]
if (!snapshotFile) {
  logger.warn({ projectId: this.projectId, pathname }, 'file gone from snapshot; skipping resync')
  return []
}
Defensive patterns

Strategy: validation

Validate before calling

const pathname = UpdateTranslator._convertPathname(update.path)
if (!snapshot.files[pathname]) { logger.warn({ path: update.path }, 'file absent from snapshot; skipping doc resync'); return }

Type guard

function fileInSnapshot(files, pathname) { return files != null && Object.prototype.hasOwnProperty.call(files, pathname) }

Try / catch

try { await expandResync(update) } catch (err) { if (err instanceof OError && err.message === 'unrecognised file: not in snapshot') { await reloadSnapshotForCurrentHistoryId(); return expandResync(update) } throw err }

Prevention

When it happens

Trigger: A resyncDocContent update references update.path that, after pathname conversion, is absent from the snapshot's files map — e.g. the file was deleted, renamed, or the snapshot was loaded for the wrong historyId.

Common situations: File renamed/deleted between resync initiation and processing; resync run against a stale snapshot (older historyId); case-sensitivity or pathname-conversion differences between web and history services.

Related errors


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