Skip to content

Empty string in external_user_id causes uniqueness constraint violation #121

@Ahmath-Gadji

Description

@Ahmath-Gadji

Description

Origin of the Error

When creating 2 users without supplying the external_user_id (so it's supposed to be None), it leads to error. Here is the definition of the field external_user_id.

external_user_id = Column(String, unique=True, nullable=True, index=True)

Although PostgreSQL allows multiple NULL values in a UNIQUE column, new users are being created with an empty string ('') instead of None.
Since PostgreSQL treats '' as a valid (non-null) value, the UNIQUE constraint is enforced, leading to:

psycopg2.errors.UniqueViolation: duplicate key value violates unique constraint "ix_users_external_user_id"
DETAIL: Key (external_user_id)=() already exists.

Cause:
FastAPI’s Swagger UI sends an empty string ('') when the field is left blank.
Only when “Send empty value” is unchecked does it send None, allowing user creation.

Reference:
[openrag/routers/users.py#L28-L43](

@router.post("/")
async def create_user(
display_name: Optional[str] = Form(None),
external_user_id: Optional[str] = Form(None),
is_admin: bool = Form(False),
vectordb=Depends(get_vectordb),
admin_user=Depends(require_admin),
):
"""
Create a new user and generate a token.
"""
user = await vectordb.create_user.remote(
display_name=display_name,
external_user_id=external_user_id,
is_admin=is_admin,
)
)


Solution

Simple solution

Ensure that empty strings are converted to None before database insertion:

@router.post("/")
async def create_user(
    display_name: Optional[str] = Form(None),
    external_user_id: Optional[str] = Form(None),
    is_admin: bool = Form(False),
    vectordb=Depends(get_vectordb),
    admin_user=Depends(require_admin),
):
    """
    Create a new user and generate a token.
    """
    user = await vectordb.create_user.remote(
        display_name=display_name,
        external_user_id=external_user_id or None,
        is_admin=is_admin,
    )
    logger.info("Created new user", user_id=user["id"])
    return JSONResponse(status_code=status.HTTP_201_CREATED, content=user)

Note

This issue is related to this commit when we replaced Query to Form: dcfd643

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions