mem0ai/mem0 · error · ValueError
Role must be either 'READER' or 'OWNER'
Error message
Role must be either 'READER' or 'OWNER'
What it means
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.
Source
Thrown at mem0/client/project.py:538
"""
Add a new member to the current project.
Args:
email: Email address of the user to add
role: Role to assign ("READER" or "OWNER")
Returns:
Dictionary containing the API response.
Raises:
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
if role not in ["READER", "OWNER"]:
raise ValueError("Role must be either 'READER' or 'OWNER'")
payload = {"email": email, "role": role}
response = self._client.post(
f"/api/v1/orgs/organizations/{self.config.org_id}/projects/{self.config.project_id}/members/",
json=payload,
)
response.raise_for_status()
capture_client_event(
"client.project.add_member",
self,
{"email": email, "role": role, "sync_type": "sync"},
)
return response.json()
@api_error_handler
def update_member(self, email: str, role: str) -> Dict[str, Any]:
"""View on GitHub (pinned to 001c235229)
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
Example fix
# before
client.project.add_member(email="a@b.com", role="admin")
# after
role = {"admin": "OWNER", "viewer": "READER"}.get(user_role)
if role:
client.project.add_member(email="a@b.com", role=role) Defensive patterns
Strategy: validation
Validate before calling
ROLE_MAP = {"admin": "OWNER", "owner": "OWNER", "reader": "READER", "viewer": "READER"}
role = ROLE_MAP.get(str(raw_role).strip().lower())
if role is None:
raise InputError("role must map to READER or OWNER")
client.project.add_member(email=email, role=role) Type guard
def is_valid_role(v: str) -> bool:
return v in ("READER", "OWNER") Try / catch
try:
client.project.add_member(email=email, role=role)
except ValueError as e:
if "Role must be" in str(e):
return bad_request("role must be READER or OWNER")
raise Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- projectId must be set to access webhooks
- projectId must be set to create a webhook
- Missing filters or schema
- Missing memoryExportId or filters
- org_id and project_id must be set to access project operatio
AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15).
Data as JSON: /api/errors/391d559758643ad5.
Report an issue: GitHub.