{"record":{"id":"e7b1c035d4d8df30","repo":"Leantime/leantime","slug":"32002","errorCode":"-32002","errorMessage":"A task referenced in the sort order could not be found.","messagePattern":"A task referenced in the sort order could not be found\\.","errorType":"exception","errorClass":"Leantime\\Core\\Exceptions\\NotFoundException","httpStatus":404,"severity":"error","filePath":"app/Domain/Projects/Services/Projects.php","lineNumber":2774,"sourceCode":"     * Validates manager+ access for every entity in the mixed payload (pgm-/ticket-\n     * prefixed and legacy numeric ids) before delegating to updateProjectSorting().\n     * Ticket ids are resolved to their project so the same manage-access rule applies.\n     *\n     * @param  array  $params  Map of (pgm-{id}|ticket-{id}|{id}) => sort position\n     * @return bool True on success (false only if the underlying write fails)\n     *\n     * @throws NotFoundException If a ticket-{id} key references a ticket that does not exist\n     * @throws AuthorizationException If the caller cannot manage any item in the payload\n     *\n     * @api\n     */\n    public function sortProjects(array $params): bool\n    {\n        foreach (array_keys($params) as $id) {\n            if (str_starts_with((string) $id, 'ticket-')) {\n                $ticket = $this->ticketRepository->getTicket((int) substr((string) $id, 7));\n                if (! $ticket) {\n                    throw new NotFoundException('A task referenced in the sort order could not be found.');\n                }\n                $projectId = (int) $ticket->projectId;\n            } elseif (str_starts_with((string) $id, 'pgm-')) {\n                $projectId = (int) substr((string) $id, 4);\n            } else {\n                $projectId = (int) $id;\n            }\n\n            if (! $this->userCanManageProject($projectId)) {\n                throw new AuthorizationException('You are not allowed to re-sort one or more of these items.');\n            }\n        }\n\n        return $this->updateProjectSorting($params);\n    }\n\n    /**\n     * Authorized JSON-RPC entry point for patching a single project (e.g. Gantt","sourceCodeStart":2756,"sourceCodeEnd":2792,"githubUrl":"https://github.com/Leantime/leantime/blob/9a9f49f1008f4782b30f6723c54228f4f992e636/app/Domain/Projects/Services/Projects.php#L2756-L2792","documentation":"Projects::sortProjects() is the authorized JSON-RPC entry point for Program Timeline (Gantt) re-sorting. Payload keys may be pgm-{id}, ticket-{id}, or bare numeric ids; ticket- keys are resolved to a real ticket via ticketRepository->getTicket(), and if that ticket does not exist it throws NotFoundException (JSON-RPC -32002). The check exists so a bogus task reference fails loudly instead of silently reordering part of the timeline.","triggerScenarios":"Sorting the Gantt after another user deleted a task that is still rendered in your stale view; a hand-built or replayed payload containing a wrong/truncated ticket id (the digits after 'ticket-' are cast to int); a ticket id from a different Leantime instance.","commonSituations":"Long-lived program-timeline tabs; concurrent editing where tasks are deleted mid-session; automation scripts replaying captured sort payloads after data changes.","solutions":["Refresh the Program Timeline so the payload only contains current task ids, then re-apply the sort","Filter the payload first: for each ticket-{id} key, confirm Tickets::getTicket((int) substr($id, 7)) returns a row before including it","Catch NotFoundException (-32002) and surface a 'task no longer exists — reload' message instead of a generic failure"],"exampleFix":"// before\n$projectsService->sortProjects($params); // throws on stale ticket-42\n\n// after\n$params = array_filter($params, function ($pos, $id) use ($ticketsService) {\n    if (str_starts_with((string) $id, 'ticket-')) {\n        return (bool) $ticketsService->getTicket((int) substr((string) $id, 7));\n    }\n    return true;\n}, ARRAY_FILTER_USE_BOTH);\n$projectsService->sortProjects($params);","handlingStrategy":"validation","validationCode":"foreach (array_keys($params) as $id) {\n    if (str_starts_with((string) $id, 'ticket-')) {\n        if (! $ticketsService->getTicket((int) substr((string) $id, 7))) {\n            unset($params[$id]); // drop stale task references pre-call\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    $ok = $projectsService->sortProjects($params);\n} catch (\\Leantime\\Core\\Exceptions\\NotFoundException $e) {\n    // -32002: a ticket-{id} key is gone — reload the timeline and re-apply\n    $board->reload();\n}","preventionTips":["Send sort payloads from freshly rendered boards; avoid replaying captured payloads","Validate every ticket-{id} key with Tickets::getTicket() before including it","On -32002, prompt a reload instead of retrying the same payload"],"tags":["projects","gantt","sorting","entity-not-found","json-rpc"],"backgroundTag":"entity-not-found","analyzedSha":"9a9f49f1008f4782b30f6723c54228f4f992e636","analyzedAt":"2026-08-21T02:37:38.966Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}