{"record":{"id":"6e74bbc1fe6e93d6","repo":"passbolt/passbolt_api","slug":"the-role-could-not-be-saved","errorCode":null,"errorMessage":"The role could not be saved.","messagePattern":"The role could not be saved\\.","errorType":"validation","errorClass":"CustomValidationException","httpStatus":400,"severity":"error","filePath":"src/Service/Roles/RolesAddService.php","lineNumber":64,"sourceCode":"        $data = array_merge($data, [\n            'description' => null,\n            'created_by' => $uac->getId(),\n            'modified_by' => $uac->getId(),\n        ]);\n\n        $role = $rolesTable->newEntity($data, ['accessibleFields' => [\n            'name' => true,\n            'description' => true,\n            'created_by' => true,\n            'modified_by' => true,\n        ]]);\n\n        try {\n            $result = $rolesTable->saveOrFail($role);\n        } catch (PersistenceFailedException $e) { // @phpstan-ignore-line\n            $errors = $e->getEntity()->getErrors();\n\n            throw new CustomValidationException(\n                __('The role could not be saved.'),\n                $errors\n            );\n        } catch (Exception $e) {\n            throw new InternalErrorException(__('Could not save the role, please try again later.'), null, $e);\n        }\n\n        $this->dispatchEvent(self::AFTER_ROLE_CREATE_SUCCESS_EVENT_NAME, [\n            'uac' => $uac,\n            'role' => $result,\n        ]);\n\n        return $result;\n    }\n}\n","sourceCodeStart":46,"sourceCodeEnd":80,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/src/Service/Roles/RolesAddService.php#L46-L80","documentation":"RolesAddService::add throws this CustomValidationException when the Roles table's saveOrFail raises a PersistenceFailedException, i.e. the new role entity failed model rules/rulesChecker before INSERT. The entity's field errors are attached so the API can report which fields were rejected.","triggerScenarios":"POST /roles with a role name that violates table validation (empty name, name longer than 255 chars, or a rulesChecker uniqueness violation on name).","commonSituations":"Creating a role whose name already exists (duplicate slug/name), posting an empty or whitespace-only name from a misbehaving client, or importing roles in bulk where one collides with an existing one.","solutions":["Read the `errors` payload in the 400 response to identify the failing field (usually name).","Verify the role name is unique: GET /roles and check for an existing entry.","Resend POST /roles with a valid, non-empty, unique name.","If persistence fails repeatedly without a clear field error, check DB connectivity/constraints and the Roles table rules."],"exampleFix":"// before\nPOST /roles {\"name\": \"admin\"}  // already exists -> 400\n// after\nPOST /roles {\"name\": \"auditor\"} // unique -> 201","handlingStrategy":"validation","validationCode":"function validRoleName(name) {\n  return typeof name === 'string' && name.trim().length > 0 && name.length <= 255;\n}\nif (!validRoleName(roleName)) throw new Error('Invalid role name before POST /roles');","typeGuard":"function isRolePayload(v) {\n  return typeof v === 'object' && v !== null && typeof v.name === 'string' && v.name.trim().length > 0;\n}","tryCatchPattern":"try {\n  await api.createRole({ name });\n} catch (e) {\n  if (e.status === 400 && e.body?.errors?.name) {\n    // duplicate or invalid name: prompt user for a new one\n  } else throw e;\n}","preventionTips":["Check GET /roles for name uniqueness before creating","Trim and require non-empty names in the UI","Keep role names within the 255-char column limit"],"tags":["roles","validation","cakephp","passbolt"],"backgroundTag":"database-write-failed","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}