{"record":{"id":"3f99b6dcc72208c9","repo":"octobercms/october","slug":"backend-lang-media-move-dest-src-match","errorCode":null,"errorMessage":"backend::lang.media.move_dest_src_match","messagePattern":"backend::lang\\.media\\.move_dest_src_match","errorType":"exception","errorClass":"ApplicationException","httpStatus":null,"severity":"error","filePath":"modules/media/widgets/MediaManager.php","lineNumber":602,"sourceCode":"\n    /**\n     * onMoveItems AJAX handler\n     * @return array\n     */\n    public function onMoveItems()\n    {\n        if (!$this->checkHasPermission('mediaDelete')) {\n            throw new ForbiddenException;\n        }\n\n        $dest = trim(Input::get('dest'));\n        if (!strlen($dest)) {\n            throw new ApplicationException(Lang::get('backend::lang.media.please_select_move_dest'));\n        }\n\n        $dest = MediaLibrary::validatePath($dest);\n        if ($dest === Input::get('originalPath')) {\n            throw new ApplicationException(Lang::get('backend::lang.media.move_dest_src_match'));\n        }\n\n        $files = Input::get('files', []);\n        if (!is_array($files)) {\n            throw new ApplicationException('Invalid input data');\n        }\n\n        $folders = Input::get('folders', []);\n        if (!is_array($folders)) {\n            throw new ApplicationException('Invalid input data');\n        }\n\n        $library = MediaLibrary::instance();\n\n        foreach ($files as $path) {\n            /*\n             * Move a single file\n             */","sourceCodeStart":584,"sourceCodeEnd":620,"githubUrl":"https://github.com/octobercms/october/blob/b608633a7e8922487d91a8161499020121c3b3bf/modules/media/widgets/MediaManager.php#L584-L620","documentation":"Thrown by onMoveItems when the validated destination equals the raw originalPath POST value, i.e. the items would be moved into the folder they are already in. dest goes through MediaLibrary::validatePath() first, so '/' normalizes and still matches originalPath='/' — the widget refuses no-op moves.","triggerScenarios":"AJAX 'onMoveItems' where dest='/photos' and originalPath='/photos' (moving items into their current folder), including both being the root '/'. Only the exact string match triggers it; moving into a subfolder of the source does not (and is not blocked here).","commonSituations":"The move dialog preselects the current folder and the user submits without changing it; scripted bulk moves that pass the source path as destination by mistake; UI bug where the selected node in the tree is the active folder.","solutions":["Select a different destination folder in the popup before confirming","In scripts, assert dest !== originalPath before issuing the request","Treat the error as benign in automation: catch it and skip the move instead of retrying"],"exampleFix":"// before\n$.request('onMoveItems', { data: { dest, originalPath, files, folders } });\n\n// after\nif (dest.trim() !== originalPath) {\n    $.request('onMoveItems', { data: { dest, originalPath, files, folders } });\n}","handlingStrategy":"validation","validationCode":"if (dest.trim() === originalPath) { showToast('Source and destination are the same'); return; }\n$.request('onMoveItems', { data: { dest, originalPath, files, folders } });","typeGuard":"function isNoOpMove(dest, originalPath) {\n  return dest.trim() === originalPath;\n}","tryCatchPattern":"catch (e) { if (/same/i.test(e.message)) { markSkipped(item); continue; } throw e; } // in batch loops","preventionTips":["Preselect nothing (not the current folder) in the destination tree","In batch automation, skip when dest equals source instead of erroring","Compare after trimming — the server trims too"],"tags":["media-manager","move","no-op","conflict","backend-ajax"],"backgroundTag":"source-destination-conflict","analyzedSha":"b608633a7e8922487d91a8161499020121c3b3bf","analyzedAt":"2026-08-21T04:24:57.515Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}