{"record":{"id":"4f686148a005998b","repo":"remotion-dev/remotion","slug":"could-not-delete-jsx-nodes","errorCode":null,"errorMessage":"Could not delete JSX nodes","messagePattern":"Could not delete JSX nodes","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/browser-studio/src/browser-studio-operations.ts","lineNumber":1320,"sourceCode":"\t\t\t\t...project,\n\t\t\t\tfiles: {\n\t\t\t\t\t...project.files,\n\t\t\t\t\t...Object.fromEntries(\n\t\t\t\t\t\tupdates.map(({fileName, result}) => [fileName, result.output]),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t};\n\t\t\tconst nodePathMutation = controller.applyMutation({\n\t\t\t\ttimelineSelection: null,\n\t\t\t\tfileName: updates.map(({fileName}) => fileName).join(', '),\n\t\t\t\tmutate: () => nextProject,\n\t\t\t\tnodePathMutationFiles: updates.map(({fileName, result}) => ({\n\t\t\t\t\tabsolutePath: fileName,\n\t\t\t\t\tremappings: result.nodePathRemappings,\n\t\t\t\t})),\n\t\t\t});\n\t\t\tif (nodePathMutation === null) {\n\t\t\t\tthrow new Error('Could not delete JSX nodes');\n\t\t\t}\n\n\t\t\treturn {success: true, nodePathMutation};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\treason: error instanceof Error ? error.message : String(error),\n\t\t\t\tstack: error instanceof Error && error.stack ? error.stack : '',\n\t\t\t};\n\t\t}\n\t};\n\n\tconst duplicateJsxNode: BrowserStudioOperations['duplicateJsxNode'] = async ({\n\t\tnodes,\n\t}) => {\n\t\ttry {\n\t\t\tif (nodes.length === 0) {\n\t\t\t\tthrow new Error('No JSX nodes were specified for duplication');","sourceCodeStart":1302,"sourceCodeEnd":1338,"githubUrl":"https://github.com/remotion-dev/remotion/blob/b2f4e34732f3c6c222ea029413e22dc402218cd7/packages/browser-studio/src/browser-studio-operations.ts#L1302-L1338","documentation":"deleteJsxNode commits the deletion through controller.applyMutation, which returns null when the mutation does not actually change the project (mutate(before) returned the identical project reference, i.e. a no-op commit with no node-path mutation to report). deleteJsxNode treats a null result as failure and throws; the catch converts it to {success: false, reason}.","triggerScenarios":"Deleting JSX nodes whose codemod output matches the existing file content (stale node paths pointing at nothing), or racing a concurrent project replacement so the committed mutation is a no-op and applyMutation yields null.","commonSituations":"Node paths captured before an edit or undo shifted the JSX; automated agents replaying delete operations against a changed project; rare defensive path — reproducible occurrences usually indicate stale node-path state.","solutions":["Verify each nodePath still resolves in the current file contents before calling deleteJsxNode.","Refresh node paths from 'sequence-node-paths-remapped' events after any mutation that remaps them.","Retry the operation once after re-reading the project; if it reproduces, capture the file source and node paths and report it as a bug."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const nodePathResolves = (source: string, nodePath: readonly number[]): boolean => {\n  // cheap structural check: file must contain JSX and the parent path must be in range\n  return source.length > 0 && nodePath.every((index) => Number.isInteger(index) && index >= 0);\n};\n\nconst staleNodes = nodes.filter((n) => !nodePathResolves(project.files[n.fileName] ?? '', n.nodePath.nodePath));\nif (staleNodes.length === nodes.length) {\n  // all paths stale — refresh from 'sequence-node-paths-remapped' before deleting\n}","typeGuard":null,"tryCatchPattern":"const result = await operations.deleteJsxNode({nodes});\nif (!result.success && result.reason === 'Could not delete JSX nodes') {\n  // node paths are stale: refresh them from remap events and rebuild the request\n}","preventionTips":["Subscribe to 'sequence-node-paths-remapped' events and update stored node paths.","Never reuse node paths captured before another mutation or undo/redo.","Check the returned success flag on every JSX operation instead of assuming success."],"tags":["jsx","codemod","node-path","stale-state"],"backgroundTag":"no-op-mutation-rejected","analyzedSha":"b2f4e34732f3c6c222ea029413e22dc402218cd7","analyzedAt":"2026-08-22T21:45:17.748Z","contentChangedAt":"2026-08-22T21:45:17.748Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}