overleaf/overleaf · error · DocRevValueError

failed to unarchive doc

Error message

failed to unarchive doc

What it means

restoreArchivedDoc calls db.docs.updateOne with a pipeline built from the stored update and expects exactly one document to match the query. If matchedCount !== 1 (0 matches), the archived doc is not present under the given query (docId/rev), so the unarchive silently failed. The library throws DocRevValueError to surface this as a failed restore rather than returning a stale result.

Source

Thrown at services/docstore/app/js/MongoManager.js:233

    _id: new ObjectId(docId),
    project_id: new ObjectId(projectId),
    rev: archivedDoc.rev,
  }
  const update = {
    $set: {
      lines: archivedDoc.lines,
      ranges: archivedDoc.ranges || {},
    },
    $unset: {
      inS3: true,
    },
  }
  const pipeline = convertUpdateToPipeline(update)
  const payloadSize = BSON.calculateObjectSize(pipeline)
  Metrics.count('mongo_docs_write', payloadSize, 1, { method: 'restore' })
  const result = await db.docs.updateOne(query, pipeline)
  if (result.matchedCount !== 1) {
    throw new Errors.DocRevValueError('failed to unarchive doc', {
      docId,
      rev: archivedDoc.rev,
    })
  }
}

async function getDocRev(docId) {
  const doc = await db.docs.findOne(
    { _id: new ObjectId(docId.toString()) },
    { projection: { rev: 1 } }
  )
  return doc && doc.rev
}

/**
 * Helper method  to support optimistic locking.
 *
 * Check that the rev of an existing doc is unchanged. If the rev has

View on GitHub (pinned to 28ad3b03b7)

Solutions

  1. Verify the doc still exists in the docs collection with the expected archived state/rev before calling restoreArchivedDoc.
  2. Handle the race by retrying: re-fetch the archived doc and call restore again if another process changed it.
  3. Check the docId is correct and the doc was actually archived (not deleted) before restoring.
  4. Catch Errors.DocRevValueError in the caller and return a 404/409 to the HTTP client instead of a 500.

Example fix

// before
await docstoreManager.unArchiveDoc(projectId, docId)
// after
try {
  await docstoreManager.unArchiveDoc(projectId, docId)
} catch (err) {
  if (err instanceof Errors.DocRevValueError) {
    return res.status(404).json({ error: 'archived doc not found or already restored' })
  }
  throw err
}
Defensive patterns

Strategy: try-catch

Validate before calling

const archived = await db.docs.findOne({ _id: docId })
if (!archived || archived.rev !== expectedRev) {
  throw new Error(`archived doc ${docId} not present with rev ${expectedRev}`)
}

Type guard

function isRestoreable(doc) {
  return doc != null && typeof doc.rev === 'number' && !Number.isNaN(doc.rev)
}

Try / catch

try {
  await MongoManager.restoreArchivedDoc(docId, rev)
} catch (err) {
  if (err instanceof Errors.DocRevValueError) {
    // doc already restored or gone — treat as 404/409, do not retry blindly
    return { status: 404 }
  }
  throw err
}

Prevention

When it happens

Trigger: Calling MongoManager.restoreArchivedDoc (e.g. via the docstore unarchive HTTP endpoint) when the doc no longer exists in the archived collection under the query, or the archived doc's rev changed between read and update, so updateOne matches 0 documents.

Common situations: Race where another process unarchives or deletes the archived doc between the fetch and the updateOne; stale docId passed by a caller; retry of an already-completed unarchive; replica-set failover causing a lost update window.

Related errors


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