overleaf/overleaf · error · Errors.InvalidError

Rollout percentage for variant defined in previous version a

Error message

Rollout percentage for variant defined in previous version as ${JSON.stringify(variant)} cannot be decreased: revert to a previous configuration instead

What it means

Thrown by _checkNewVariantsConfiguration when the new configuration lowers a variant's rolloutPercent below its value in the previous version. Rollout percentages may only stay flat or increase; to reduce exposure the operator must revert to an earlier version instead of editing forward.

Source

Thrown at services/web/app/src/Features/SplitTests/SplitTestManager.mjs:464

  )
  if (totalRolloutPercentage > 100) {
    throw new Errors.InvalidError(
      `Total variants rollout percentage cannot exceed 100`
    )
  }
  for (const variant of variants) {
    const newVariantConfiguration = _.find(newVariantsConfiguration, {
      name: variant.name,
    })
    if (!newVariantConfiguration) {
      throw new Errors.InvalidError(
        `Variant defined in previous version as ${JSON.stringify(
          variant
        )} cannot be removed in new configuration: either set it inactive or create a new split test`
      )
    }
    if (newVariantConfiguration.rolloutPercent < variant.rolloutPercent) {
      throw new Errors.InvalidError(
        `Rollout percentage for variant defined in previous version as ${JSON.stringify(
          variant
        )} cannot be decreased: revert to a previous configuration instead`
      )
    }
    if (variant.userLimit !== undefined) {
      // Existing variant has a user limit - can only increase it
      if (
        newVariantConfiguration.userLimit !== undefined &&
        newVariantConfiguration.userLimit < variant.userLimit
      ) {
        throw new Errors.InvalidError(
          `User limit for variant '${variant.name}' cannot be decreased: revert to a previous configuration instead`
        )
      }
    } else {
      // Existing variant has no user limit - cannot add one
      if (newVariantConfiguration.userLimit !== undefined) {

View on GitHub (pinned to 28ad3b03b7)

Solutions

  1. Use revertToPreviousVersion to return to an earlier configuration instead of decreasing percentages via a new version.
  2. Keep each variant's rolloutPercent >= its previous value in the new configuration.
  3. Check current version values via SplitTestUtils.getCurrentVersion(splitTest) before drafting the new config.
  4. Catch Errors.InvalidError (400) and surface the offending variant and percentages.

Example fix

// before
await manager.createNewVersion(name, userId, [{ name: 'variant', rolloutPercent: 25 }]) // was 50
// after
const cur = SplitTestUtils.getCurrentVersion(splitTest)
await manager.revertToPreviousVersion(name, cur.versionNumber) // revert instead
Defensive patterns

Strategy: validation

Validate before calling

const current = SplitTestUtils.getCurrentVersion(splitTest)
for (const v of current.variants) {
  const nv = cfg.find(c => c.name === v.name)
  if (nv && nv.rolloutPercent < v.rolloutPercent) {
    throw new Error(`variant '${v.name}' cannot decrease from ${v.rolloutPercent} to ${nv.rolloutPercent}`)
  }
}

Try / catch

try {
  await manager.createNewVersion(name, userId, cfg)
} catch (err) {
  if (err instanceof Errors.InvalidError && err.message.includes('cannot be decreased')) {
    // call revertToPreviousVersion instead
  } else throw err
}

Prevention

When it happens

Trigger: Calling createNewVersion where for some variant newVariantConfiguration.rolloutPercent < variant.rolloutPercent from the current version, e.g. lowering a botched 100% rollout back to 25% via a new version.

Common situations: Trying to walk back an over-rolled variant; admin UI drafts based on stale data showing a higher previous percent; automated rebalancing that shrinks one variant to grow another.

Related errors


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