{"record":{"id":"f1c12585830edac3","repo":"Leantime/leantime","slug":"32005","errorCode":"-32005","errorMessage":"Client exists already","messagePattern":"Client exists already","errorType":"exception","errorClass":"Leantime\\Core\\Exceptions\\EntityExistsException","httpStatus":409,"severity":"error","filePath":"app/Domain/Clients/Services/Clients.php","lineNumber":234,"sourceCode":"     * that previously lived in the controller.\n     *\n     * @param  array  $values  Client data to create (requires a non-empty 'name')\n     * @return int Id of the newly created client\n     *\n     * @throws MissingParameterException When the client name is empty\n     * @throws EntityExistsException When a client with the same name/street already exists\n     *\n     * @api\n     */\n    #[RequiresPermission(ClientsPermissions::CREATE, global: true)]\n    public function createClient(array $values): int\n    {\n        if (($values['name'] ?? '') === '') {\n            throw new MissingParameterException('Client name not specified');\n        }\n\n        if ($this->isClient($values) === true) {\n            throw new EntityExistsException('Client exists already');\n        }\n\n        return (int) $this->clientRepository->addClient($values);\n    }\n\n    /**\n     * Updates an existing client after validating the name is present.\n     *\n     * @param  array  $values  Client data including 'id' key (requires a non-empty 'name')\n     * @return bool Returns true on success, false on failure\n     *\n     * @throws MissingParameterException When the client name is empty\n     *\n     * @api\n     */\n    #[RequiresPermission(ClientsPermissions::EDIT, global: true)]\n    public function updateClient(array $values): bool\n    {","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/Leantime/leantime/blob/9a9f49f1008f4782b30f6723c54228f4f992e636/app/Domain/Clients/Services/Clients.php#L216-L252","documentation":"createClient() calls isClient($values) (which delegates to the repository) to check whether a client with the same name/street combination already exists; if so it throws EntityExistsException, mapped to JSON-RPC code -32005. isClient() is itself an @api method, so callers can pre-check for duplicates before attempting the create.","triggerScenarios":"Calling createClient with a name+street combination that matches an existing zp_clients row; double-submitting the create form (second POST arrives after the first succeeded); retrying an API call after a timeout when the first attempt actually committed.","commonSituations":"Create forms without POST-redirect-GET or a disabled submit button; re-running a data import script that is not idempotent; two users creating the same client name concurrently.","solutions":["Pre-check with leantime.rpc.Clients.Clients.isClient using the same payload; if it returns true, call updateClient on the existing client instead of creating","Make the client name unique (or correct the street value) if the duplicate is unintentional","Protect the UI against double submits (disable the button / POST-redirect-GET) and catch EntityExistsException (-32005) to show a friendly 'already exists' message"],"exampleFix":"// before\n$newId = $clientsService->createClient($values);\n\n// after\nif ($clientsService->isClient($values)) {\n    throw new \\RuntimeException('Client already exists; use updateClient');\n}\n$newId = $clientsService->createClient($values);","handlingStrategy":"validation","validationCode":"if ($clientsService->isClient($values)) {\n    // duplicate name/street — update the existing client instead of creating\n    $existing = $clientsService->getAll();\n    // ...find and update, or prompt the user\n}","typeGuard":null,"tryCatchPattern":"try {\n    $id = $clientsService->createClient($values);\n} catch (\\Leantime\\Core\\Exceptions\\EntityExistsException $e) {\n    // JSON-RPC -32005: offer 'edit existing client' instead of create\n    $form->addError('name', 'A client with this name/street already exists');\n}","preventionTips":["Call the isClient @api pre-check with the same payload before every createClient","Disable the submit button on first click and use POST-redirect-GET to stop double submits","Make imports idempotent: on -32005, resolve the existing client and switch to updateClient"],"tags":["clients","duplicate","entity-exists","json-rpc","api"],"backgroundTag":"duplicate-entity","analyzedSha":"9a9f49f1008f4782b30f6723c54228f4f992e636","analyzedAt":"2026-08-21T02:37:38.966Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}