overleaf/overleaf · error · InvalidError
Cannot switch an archived split test to next phase
Error message
Cannot switch an archived split test to next phase
What it means
Thrown by switchToNextPhase in SplitTestManager.mjs when the split test named in the request exists but has the `archived` flag set. Archived tests are treated as finished/retired and the manager refuses any lifecycle mutation, including phase switches. It surfaces as an InvalidError (HTTP 400) so callers know the request is well-formed but the target state forbids it.
Source
Thrown at services/web/app/src/Features/SplitTests/SplitTestManager.mjs:237
} catch (error) {
throw OError.tag(error, 'Failed to merge all split tests, merged set was', {
merged,
})
}
}
async function switchToNextPhase(
{ name, comment, targetPhase, labsUserLimit, clearUserLimit },
userId
) {
const splitTest = await getSplitTest({ name })
if (!splitTest) {
throw new Errors.NotFoundError(
`Cannot switch split test with ID '${name}' to next phase: not found`
)
}
if (splitTest.archived) {
throw new Errors.InvalidError(
'Cannot switch an archived split test to next phase',
{
name,
}
)
}
const lastVersionCopy = SplitTestUtils.getCurrentVersion(splitTest).toObject()
lastVersionCopy.versionNumber++
const currentPhase = lastVersionCopy.phase
// Determine and validate target phase
if (targetPhase) {
const validTransitions = {
[ALPHA_PHASE]: [LABS_PHASE, BETA_PHASE],
[LABS_PHASE]: [BETA_PHASE],
[BETA_PHASE]: [RELEASE_PHASE],
}View on GitHub (pinned to 28ad3b03b7)
Solutions
- Fetch the test first and check `archived` before calling switchToNextPhase; skip archived tests in batch jobs.
- Unarchive the test (clear the `archived` flag) if it genuinely needs to be reactivated, then retry the phase switch.
- Filter admin UI / API queries with archived=false so archived tests cannot be selected.
Example fix
// before
await splitTestManager.promises.switchToNextPhase({ name: 'my-test' })
// after
const test = await splitTestManager.promises.getSplitTest({ name: 'my-test' })
if (!test.archived) {
await splitTestManager.promises.switchToNextPhase({ name: 'my-test' })
} Defensive patterns
Strategy: validation
Validate before calling
const test = await splitTestManager.promises.getSplitTest({ name })
if (!test || test.archived) throw new Error(`test '${name}' missing or archived`) Type guard
function canSwitchPhase(test) {
return test != null && test.archived === false
} Try / catch
try {
await switchToNextPhase({ name })
} catch (err) {
if (err instanceof Errors.InvalidError && /archived/.test(err.message)) {
// skip or unarchive
} else throw err
} Prevention
- Filter archived: false in any list used to drive phase switches
- Pre-fetch the test and check the archived flag before mutating
- Disable advance/revert actions in admin UI for archived tests
When it happens
Trigger: Calling switchToNextPhase({ name, ... }) where getSplitTest({ name }) resolves a test whose `archived` property is true. Happens via the admin phase-switch endpoint on an archived test, or scripted automation iterating over all tests including archived ones.
Common situations: An admin UI list that does not filter out archived tests; a batch script advancing every test to release; a test archived earlier (e.g. after release) that someone tries to re-advance; stale local data where the test was archived by another operator.
Related errors
- Cannot revert an archived split test to previous version
- Cannot update an archived split test
- Cannot revert split test with ID '${name}' to previous versi
- Cannot update with different phase - use /switch-to-next-pha
- Cannot switch split test with ID '${name}' to next phase: no
AI-assisted analysis of overleaf/overleaf@28ad3b03b7 (2026-09-03).
Data as JSON: /api/errors/4f2ed4e7a13d4f22.
Report an issue: GitHub.