{"record":{"id":"9151a04e7855490f","repo":"remotion-dev/remotion","slug":"element-source-changed-during-installation","errorCode":null,"errorMessage":"Element source changed during installation","messagePattern":"Element source changed during installation","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/browser-studio/src/browser-studio-operations.ts","lineNumber":2072,"sourceCode":"\t\t\t\t\trequest.expectedFileState !== null &&\n\t\t\t\t\t!expectedFileStateMatches({\n\t\t\t\t\t\tactual: plan.expectedFileState,\n\t\t\t\t\t\texpected: request.expectedFileState,\n\t\t\t\t\t})\n\t\t\t\t) {\n\t\t\t\t\tif (plan.existingSource !== null) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\ttype: 'file-conflict',\n\t\t\t\t\t\t\tconflict: {\n\t\t\t\t\t\t\t\texistingSource: plan.existingSource,\n\t\t\t\t\t\t\t\tfilePath: plan.filePath,\n\t\t\t\t\t\t\t\tincomingSource: request.element.sourceCode,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tthrow new Error('Element source changed during installation');\n\t\t\t\t}\n\n\t\t\t\tif (!request.overwriteExisting && plan.existingSource !== null) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\ttype: 'file-conflict',\n\t\t\t\t\t\tconflict: {\n\t\t\t\t\t\t\texistingSource: plan.existingSource,\n\t\t\t\t\t\t\tfilePath: plan.filePath,\n\t\t\t\t\t\t\tincomingSource: request.element.sourceCode,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tconst installedDependencies = await resolveElementDependencies(\n\t\t\t\t\trequest.element.dependencies,\n\t\t\t\t);\n\t\t\t\tconst installationMode = request.element.installationMode ?? 'wrapped';","sourceCodeStart":2054,"sourceCodeEnd":2090,"githubUrl":"https://github.com/remotion-dev/remotion/blob/b2f4e34732f3c6c222ea029413e22dc402218cd7/packages/browser-studio/src/browser-studio-operations.ts#L2054-L2090","documentation":"Surfaced by insertElement in @remotion/browser-studio (as `{success: false, type: 'error', reason}`) when the caller passed a non-null `expectedFileState` that no longer matches the plan's actual file state, AND the plan has no `existingSource` (so the structured 'file-conflict' response is impossible). expectedFileState is an optimistic-concurrency token from prepareElementInstall: it proves the element file on disk is still in the state the client last saw. A mismatch means the element file changed by other means between prepare and install.","triggerScenarios":"Calling `insertElement({element, compositionFile, compositionId, expectedFileState, ...})` where expectedFileState came from an earlier prepareElementInstall and the target file has since been edited, restored, or re-scaffolded; passing an expectedFileState belonging to a different element file path.","commonSituations":"User edits the element file in an editor between previewing and installing the Element; two installs of related Elements race; an undo restored an older version of the element file mid-install.","solutions":["Re-run prepareElementInstall to get a fresh expectedFileState and repeat the install with it","Pass `expectedFileState: null` to skip the staleness check when overwrite is acceptable","Handle the 'file-conflict' response type (existingSource present) by asking the user to overwrite or keep both","Avoid mutating element files while an install is in flight"],"exampleFix":"// before\nawait operations.insertElement({\n  element,\n  compositionFile,\n  compositionId,\n  expectedFileState: stalePlan.expectedFileState, // from an old prepare call\n  overwriteExisting: false,\n});\n\n// after\nconst plan = await operations.prepareElementInstall({element, compositionFile, compositionId});\nif (!plan.success) throw new Error(plan.reason);\nconst res = await operations.insertElement({\n  element,\n  compositionFile,\n  compositionId,\n  expectedFileState: plan.plan.expectedFileState, // fresh token\n  overwriteExisting: false,\n});\nif (!res.success && res.type === 'file-conflict') {\n  // ask user, then retry with overwriteExisting: true\n}","handlingStrategy":"validation","validationCode":"const plan = await operations.prepareElementInstall({element, compositionFile, compositionId});\nif (!plan.success) throw new Error(plan.reason);\n// use the fresh token; pass null to skip the check when overwriting is fine\nconst res = await operations.insertElement({element, compositionFile, compositionId, expectedFileState: plan.plan.expectedFileState, overwriteExisting: false});\nif (!res.success && res.type === 'file-conflict') { /* ask user, retry with overwriteExisting: true */ }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair insertElement with a just-fetched prepareElementInstall token; never reuse tokens across user actions","Handle both failure shapes: type 'file-conflict' (structured) and type 'error' with this reason","Lock element files against manual edits while an install is in flight"],"tags":["elements","concurrency","file-conflict","insert","browser-studio"],"backgroundTag":"optimistic-concurrency-conflict","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"}