{"record":{"id":"b677cdef6725a169","repo":"overleaf/overleaf","slug":"rollout-percentage-for-variant-defined-in-previous","errorCode":null,"errorMessage":"Rollout percentage for variant defined in previous version as ${JSON.stringify(variant)} cannot be decreased: revert to a previous configuration instead","messagePattern":"Rollout percentage for variant defined in previous version as (.+?) cannot be decreased: revert to a previous configuration instead","errorType":"validation","errorClass":"Errors.InvalidError","httpStatus":400,"severity":"error","filePath":"services/web/app/src/Features/SplitTests/SplitTestManager.mjs","lineNumber":464,"sourceCode":"  )\n  if (totalRolloutPercentage > 100) {\n    throw new Errors.InvalidError(\n      `Total variants rollout percentage cannot exceed 100`\n    )\n  }\n  for (const variant of variants) {\n    const newVariantConfiguration = _.find(newVariantsConfiguration, {\n      name: variant.name,\n    })\n    if (!newVariantConfiguration) {\n      throw new Errors.InvalidError(\n        `Variant defined in previous version as ${JSON.stringify(\n          variant\n        )} cannot be removed in new configuration: either set it inactive or create a new split test`\n      )\n    }\n    if (newVariantConfiguration.rolloutPercent < variant.rolloutPercent) {\n      throw new Errors.InvalidError(\n        `Rollout percentage for variant defined in previous version as ${JSON.stringify(\n          variant\n        )} cannot be decreased: revert to a previous configuration instead`\n      )\n    }\n    if (variant.userLimit !== undefined) {\n      // Existing variant has a user limit - can only increase it\n      if (\n        newVariantConfiguration.userLimit !== undefined &&\n        newVariantConfiguration.userLimit < variant.userLimit\n      ) {\n        throw new Errors.InvalidError(\n          `User limit for variant '${variant.name}' cannot be decreased: revert to a previous configuration instead`\n        )\n      }\n    } else {\n      // Existing variant has no user limit - cannot add one\n      if (newVariantConfiguration.userLimit !== undefined) {","sourceCodeStart":446,"sourceCodeEnd":482,"githubUrl":"https://github.com/overleaf/overleaf/blob/28ad3b03b71cb4311decdcb55c36b33ec10d72db/services/web/app/src/Features/SplitTests/SplitTestManager.mjs#L446-L482","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use revertToPreviousVersion to return to an earlier configuration instead of decreasing percentages via a new version.","Keep each variant's rolloutPercent >= its previous value in the new configuration.","Check current version values via SplitTestUtils.getCurrentVersion(splitTest) before drafting the new config.","Catch Errors.InvalidError (400) and surface the offending variant and percentages."],"exampleFix":"// before\nawait manager.createNewVersion(name, userId, [{ name: 'variant', rolloutPercent: 25 }]) // was 50\n// after\nconst cur = SplitTestUtils.getCurrentVersion(splitTest)\nawait manager.revertToPreviousVersion(name, cur.versionNumber) // revert instead","handlingStrategy":"validation","validationCode":"const current = SplitTestUtils.getCurrentVersion(splitTest)\nfor (const v of current.variants) {\n  const nv = cfg.find(c => c.name === v.name)\n  if (nv && nv.rolloutPercent < v.rolloutPercent) {\n    throw new Error(`variant '${v.name}' cannot decrease from ${v.rolloutPercent} to ${nv.rolloutPercent}`)\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await manager.createNewVersion(name, userId, cfg)\n} catch (err) {\n  if (err instanceof Errors.InvalidError && err.message.includes('cannot be decreased')) {\n    // call revertToPreviousVersion instead\n  } else throw err\n}","preventionTips":["Treat rollout percentages as monotonic non-decreasing","Use revertToPreviousVersion to reduce exposure","Compare against current version values before submitting a draft"],"tags":["split-tests","rollout-percentage","monotonic-rollout","validation"],"backgroundTag":"rollout-percentage-decrease-forbidden","analyzedSha":"28ad3b03b71cb4311decdcb55c36b33ec10d72db","analyzedAt":"2026-09-03T02:10:22.807Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T07:17:11.731Z"}