Leantime/leantime · error · Leantime\Core\Exceptions\NotFoundException

-32002

-32002

Error message

A task referenced in the sort order could not be found.

What it means

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.

Source

Thrown at app/Domain/Projects/Services/Projects.php:2774

     * Validates manager+ access for every entity in the mixed payload (pgm-/ticket-
     * prefixed and legacy numeric ids) before delegating to updateProjectSorting().
     * Ticket ids are resolved to their project so the same manage-access rule applies.
     *
     * @param  array  $params  Map of (pgm-{id}|ticket-{id}|{id}) => sort position
     * @return bool True on success (false only if the underlying write fails)
     *
     * @throws NotFoundException If a ticket-{id} key references a ticket that does not exist
     * @throws AuthorizationException If the caller cannot manage any item in the payload
     *
     * @api
     */
    public function sortProjects(array $params): bool
    {
        foreach (array_keys($params) as $id) {
            if (str_starts_with((string) $id, 'ticket-')) {
                $ticket = $this->ticketRepository->getTicket((int) substr((string) $id, 7));
                if (! $ticket) {
                    throw new NotFoundException('A task referenced in the sort order could not be found.');
                }
                $projectId = (int) $ticket->projectId;
            } elseif (str_starts_with((string) $id, 'pgm-')) {
                $projectId = (int) substr((string) $id, 4);
            } else {
                $projectId = (int) $id;
            }

            if (! $this->userCanManageProject($projectId)) {
                throw new AuthorizationException('You are not allowed to re-sort one or more of these items.');
            }
        }

        return $this->updateProjectSorting($params);
    }

    /**
     * Authorized JSON-RPC entry point for patching a single project (e.g. Gantt

View on GitHub (pinned to 9a9f49f100)

Solutions

  1. Refresh the Program Timeline so the payload only contains current task ids, then re-apply the sort
  2. Filter the payload first: for each ticket-{id} key, confirm Tickets::getTicket((int) substr($id, 7)) returns a row before including it
  3. Catch NotFoundException (-32002) and surface a 'task no longer exists — reload' message instead of a generic failure

Example fix

// before
$projectsService->sortProjects($params); // throws on stale ticket-42

// after
$params = array_filter($params, function ($pos, $id) use ($ticketsService) {
    if (str_starts_with((string) $id, 'ticket-')) {
        return (bool) $ticketsService->getTicket((int) substr((string) $id, 7));
    }
    return true;
}, ARRAY_FILTER_USE_BOTH);
$projectsService->sortProjects($params);
Defensive patterns

Strategy: validation

Validate before calling

foreach (array_keys($params) as $id) {
    if (str_starts_with((string) $id, 'ticket-')) {
        if (! $ticketsService->getTicket((int) substr((string) $id, 7))) {
            unset($params[$id]); // drop stale task references pre-call
        }
    }
}

Try / catch

try {
    $ok = $projectsService->sortProjects($params);
} catch (\Leantime\Core\Exceptions\NotFoundException $e) {
    // -32002: a ticket-{id} key is gone — reload the timeline and re-apply
    $board->reload();
}

Prevention

When it happens

Trigger: 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.

Common situations: Long-lived program-timeline tabs; concurrent editing where tasks are deleted mid-session; automation scripts replaying captured sort payloads after data changes.

Related errors


AI-assisted analysis of Leantime/leantime@9a9f49f100 (2026-08-21). Data as JSON: /api/errors/e7b1c035d4d8df30. Report an issue: GitHub.