{"record":{"id":"9d7566fef7f9b2e2","repo":"langflow-ai/langflow","slug":"assignment-already-exists-for-this-user-role-domai","errorCode":null,"errorMessage":"Assignment already exists for this user/role/domain","messagePattern":"Assignment already exists for this user/role/domain","errorType":"http","errorClass":"HTTPException","httpStatus":409,"severity":"warning","filePath":"src/backend/base/langflow/api/v1/authz_role_assignments.py","lineNumber":114,"sourceCode":"        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=\"user_id not found\")\n    role = await session.get(AuthzRole, payload.role_id)\n    if role is None:\n        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=\"role_id not found\")\n\n    assignment = AuthzRoleAssignment(\n        user_id=payload.user_id,\n        role_id=payload.role_id,\n        domain_type=payload.domain_type,\n        domain_id=payload.domain_id,\n        assigned_at=datetime.now(timezone.utc),\n        assigned_by=current_user.id,\n    )\n    session.add(assignment)\n    try:\n        await session.commit()\n    except IntegrityError as exc:\n        await session.rollback()\n        raise HTTPException(\n            status_code=status.HTTP_409_CONFLICT,\n            detail=\"Assignment already exists for this user/role/domain\",\n        ) from exc\n    await session.refresh(assignment)\n    await safe_invalidate_user(\n        get_authorization_service(),\n        payload.user_id,\n        op=\"role_assignment:create\",\n    )\n    await audit_decision(\n        user_id=current_user.id,\n        action=\"role_assignment:create\",\n        obj=f\"user:{payload.user_id}\",\n        result=\"allow\",\n        details={\n            \"assignment_id\": str(assignment.id),\n            \"role_id\": str(payload.role_id),\n            \"role_name\": role.name,","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/langflow-ai/langflow/blob/976ec789d2886a86de109c044d089d68e96c9a35/src/backend/base/langflow/api/v1/authz_role_assignments.py#L96-L132","documentation":"Raised by POST /api/v1/authz/role-assignments when session.commit() raises SQLAlchemy IntegrityError, almost always the unique constraint on (user_id, role_id, domain_type, domain_id). The route rolls back and returns HTTP 409 'Assignment already exists for this user/role/domain'.","triggerScenarios":"POSTing the same user/role/domain combination twice — e.g. double-submit, retry after a network timeout where the first request actually committed, or two admins assigning the same role concurrently.","commonSituations":"Non-idempotent retry logic in clients, race between UI tabs, or provisioning scripts run twice without existence checks. The unique constraint at the DB level is the final guard; the API converts it to a clean 409.","solutions":["On 409, treat as success if the goal is 'user has this role' — fetch assignments and verify","Make the client idempotent: GET the assignment list first and skip if the tuple already exists","Disable the submit button / debounce to prevent double posts","For concurrent provisioning, catch 409 explicitly instead of failing the batch"],"exampleFix":"// before\nawait api.post('/authz/role-assignments', payload);\n\n// after\ntry {\n  await api.post('/authz/role-assignments', payload);\n} catch (e) {\n  if (e.status !== 409) throw e; // 409 = already assigned, desired end state\n}","handlingStrategy":"try-catch","validationCode":"const existing = await api.get('/authz/role-assignments', { params: { user_id, role_id } });\nconst dup = existing.some(a => a.user_id === user_id && a.role_id === role_id && a.domain_type === domain_type && a.domain_id === domain_id);\nif (dup) return;","typeGuard":null,"tryCatchPattern":"try { await createAssignment(payload); }\ncatch (e) { if (e.status !== 409) throw e; /* already assigned = goal state */ }","preventionTips":["Make assignment creation idempotent by checking for an existing tuple first","Debounce submit buttons to prevent double posts","On network timeout, verify with a GET before retrying the POST"],"tags":["authz","rbac","http-409","integrity-error","idempotency"],"backgroundTag":null,"analyzedSha":"976ec789d2886a86de109c044d089d68e96c9a35","analyzedAt":"2026-08-14T18:23:12.227Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}