{"record":{"id":"20ffd030c9216e5e","repo":"remotion-dev/remotion","slug":"a-remotion-upgrade-is-already-in-progress","errorCode":null,"errorMessage":"A Remotion upgrade is already in progress.","messagePattern":"A Remotion upgrade is already in progress\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/studio-server/src/preview-server/routes/upgrade-remotion.ts","lineNumber":18,"sourceCode":"import {spawn} from 'node:child_process';\nimport path from 'node:path';\nimport {RenderInternals} from '@remotion/renderer';\nimport type {ApiRoutes} from '@remotion/studio-shared';\nimport type {ApiHandler} from '../api-types';\n\nlet upgrading = false;\n\nexport const handleUpgradeRemotion: ApiHandler<\n\tApiRoutes['/api/upgrade-remotion']['Request'],\n\tApiRoutes['/api/upgrade-remotion']['Response']\n> = async ({remotionRoot, logLevel, input: {version}}) => {\n\tif (typeof version !== 'string' || !/^\\d+\\.\\d+\\.\\d+$/.test(version)) {\n\t\tthrow new Error('Invalid Remotion version.');\n\t}\n\n\tif (upgrading) {\n\t\tthrow new Error('A Remotion upgrade is already in progress.');\n\t}\n\n\tupgrading = true;\n\ttry {\n\t\tconst cliPackage = require.resolve('@remotion/cli/package.json', {\n\t\t\tpaths: [remotionRoot],\n\t\t});\n\t\tconst cli = path.join(path.dirname(cliPackage), 'remotion-cli.js');\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst child = spawn(\n\t\t\t\tprocess.execPath,\n\t\t\t\t[cli, 'upgrade', `--version=${version}`],\n\t\t\t\t{\n\t\t\t\t\tcwd: remotionRoot,\n\t\t\t\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\t\t\t\tenv: {...process.env, CI: '1'},\n\t\t\t\t},\n\t\t\t);","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/remotion-dev/remotion/blob/b2f4e34732f3c6c222ea029413e22dc402218cd7/packages/studio-server/src/preview-server/routes/upgrade-remotion.ts#L1-L36","documentation":"The upgrade endpoint serializes upgrades with a module-level `upgrading` flag. If an upgrade is already running in the Studio server process, a second concurrent request is rejected with this error until the first finishes.","triggerScenarios":"Issuing a second /api/upgrade-remotion request while a previous upgrade (npm install of @remotion/cli etc.) is still executing in the same Studio server process.","commonSituations":"Double-clicking an upgrade button, retry loops firing while the first upgrade is in flight, multiple browser tabs triggering upgrades simultaneously.","solutions":["Wait for the in-progress upgrade to complete, then retry.","Check the Studio terminal/logs for the running upgrade's progress or failure before retrying.","Restart the Studio dev server if a crashed upgrade left the flag stuck (the flag resets only on process restart in that case).","Trigger upgrades from a single place — avoid concurrent requests from multiple tabs or scripts."],"exampleFix":"// before\nPromise.all([upgradeRemotion('4.0.2'), upgradeRemotion('4.0.3')]);\n// after\nawait upgradeRemotion('4.0.3');","handlingStrategy":"try-catch","validationCode":"// client-side single-flight guard\nlet upgrading = false;\nasync function safeUpgrade(v: string) {\n  if (upgrading) return;\n  upgrading = true;\n  try { await upgradeRemotion(v); } finally { upgrading = false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await upgradeRemotion(version);\n} catch (e) {\n  if (e.message.includes('already in progress')) {\n    await waitForUpgradeCompletion(); // poll logs, then retry once\n  } else throw e;\n}","preventionTips":["Trigger upgrades from a single UI/tab only.","Debounce/dedupe upgrade requests client-side.","Monitor the Studio terminal for the running upgrade before retrying.","If the flag seems stuck, restart the Studio server."],"tags":["concurrency","upgrade","api"],"backgroundTag":"invalid-state-transition","analyzedSha":"b2f4e34732f3c6c222ea029413e22dc402218cd7","analyzedAt":"2026-09-09T13:27:51.281Z","contentChangedAt":"2026-09-09T13:27:51.281Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}