{"record":{"id":"391d559758643ad5","repo":"mem0ai/mem0","slug":"role-must-be-either-reader-or-owner","errorCode":null,"errorMessage":"Role must be either 'READER' or 'OWNER'","messagePattern":"Role must be either 'READER' or 'OWNER'","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mem0/client/project.py","lineNumber":538,"sourceCode":"        \"\"\"\n        Add a new member to the current project.\n\n        Args:\n            email: Email address of the user to add\n            role: Role to assign (\"READER\" or \"OWNER\")\n\n        Returns:\n            Dictionary containing the API response.\n\n        Raises:\n            ValidationError: If the input data is invalid.\n            AuthenticationError: If authentication fails.\n            RateLimitError: If rate limits are exceeded.\n            NetworkError: If network connectivity issues occur.\n            ValueError: If org_id or project_id are not set.\n        \"\"\"\n        if role not in [\"READER\", \"OWNER\"]:\n            raise ValueError(\"Role must be either 'READER' or 'OWNER'\")\n\n        payload = {\"email\": email, \"role\": role}\n\n        response = self._client.post(\n            f\"/api/v1/orgs/organizations/{self.config.org_id}/projects/{self.config.project_id}/members/\",\n            json=payload,\n        )\n        response.raise_for_status()\n        capture_client_event(\n            \"client.project.add_member\",\n            self,\n            {\"email\": email, \"role\": role, \"sync_type\": \"sync\"},\n        )\n        return response.json()\n\n    @api_error_handler\n    def update_member(self, email: str, role: str) -> Dict[str, Any]:\n        \"\"\"","sourceCodeStart":520,"sourceCodeEnd":556,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/mem0/client/project.py#L520-L556","documentation":"The sync add_member() validates that role is exactly 'READER' or 'OWNER' (case-sensitive list membership) before POSTing to the project members endpoint. Any other string — including lowercase variants — raises ValueError client-side. The async twin behaves identically at project.py:862.","triggerScenarios":"Calling `client.project.add_member(email, role=\"reader\")`, role=\"admin\", role=\"READER \" (trailing space), or role=None. Lowercase 'owner' fails too because the comparison is exact.","commonSituations":"Piping a role chosen from a free-text dropdown; using lowercase convention from your own RBAC system; role values read from a CSV with stray whitespace or different casing.","solutions":["Pass role='READER' or role='OWNER' exactly, in uppercase","Normalize your internal role strings with .strip().upper() and map non-matching roles before calling","Constrain the UI/API layer to a two-option enum for roles"],"exampleFix":"# before\nclient.project.add_member(email=\"a@b.com\", role=\"admin\")\n\n# after\nrole = {\"admin\": \"OWNER\", \"viewer\": \"READER\"}.get(user_role)\nif role:\n    client.project.add_member(email=\"a@b.com\", role=role)","handlingStrategy":"validation","validationCode":"ROLE_MAP = {\"admin\": \"OWNER\", \"owner\": \"OWNER\", \"reader\": \"READER\", \"viewer\": \"READER\"}\nrole = ROLE_MAP.get(str(raw_role).strip().lower())\nif role is None:\n    raise InputError(\"role must map to READER or OWNER\")\nclient.project.add_member(email=email, role=role)","typeGuard":"def is_valid_role(v: str) -> bool:\n    return v in (\"READER\", \"OWNER\")","tryCatchPattern":"try:\n    client.project.add_member(email=email, role=role)\nexcept ValueError as e:\n    if \"Role must be\" in str(e):\n        return bad_request(\"role must be READER or OWNER\")\n    raise","preventionTips":["Restrict the role field in your request schema to a Literal/enum of the two uppercase values","Normalize with .strip().upper() and map from your internal role names at the boundary"],"tags":["validation","hosted-api","members","roles"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}