{"record":{"id":"2aeb2761d12ba007","repo":"flowable/flowable-engine","slug":"only-one-of-user-or-group-can-be-used-to-create-an-2aeb27","errorCode":null,"errorMessage":"Only one of user or group can be used to create an identity link.","messagePattern":"Only one of user or group can be used to create an identity link\\.","errorType":"http","errorClass":"FlowableIllegalArgumentException","httpStatus":400,"severity":"warning","filePath":"modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/task/TaskIdentityLinkCollectionResource.java","lineNumber":75,"sourceCode":"\n    @ApiOperation(value = \"Create an identity link on a task\", tags = { \"Task Identity Links\" }, nickname = \"createTaskInstanceIdentityLinks\",\n            notes = \"It is possible to add either a user or a group.\", code = 201)\n    @ApiResponses(value = {\n            @ApiResponse(code = 201, message = \"Indicates the task was found and the identity link was created.\"),\n            @ApiResponse(code = 404, message = \"Indicates the requested task was not found or the task does not have the requested identityLink. The status contains additional information about this error.\")\n    })\n    @PostMapping(value = \"/runtime/tasks/{taskId}/identitylinks\", produces = \"application/json\")\n    @ResponseStatus(HttpStatus.CREATED)\n    public RestIdentityLink createIdentityLink(@ApiParam(name = \"taskId\") @PathVariable(\"taskId\") String taskId, @RequestBody RestIdentityLink identityLink) {\n\n        Task task = getTaskFromRequestWithoutAccessCheck(taskId);\n\n        if (identityLink.getGroup() == null && identityLink.getUser() == null) {\n            throw new FlowableIllegalArgumentException(\"A group or a user is required to create an identity link.\");\n        }\n\n        if (identityLink.getGroup() != null && identityLink.getUser() != null) {\n            throw new FlowableIllegalArgumentException(\"Only one of user or group can be used to create an identity link.\");\n        }\n\n        if (identityLink.getType() == null) {\n            throw new FlowableIllegalArgumentException(\"The identity link type is required.\");\n        }\n\n        if (restApiInterceptor != null) {\n            restApiInterceptor.createTaskIdentityLink(task, identityLink);\n        }\n\n        if (identityLink.getGroup() != null) {\n            taskService.addGroupIdentityLink(task.getId(), identityLink.getGroup(), identityLink.getType());\n        } else {\n            taskService.addUserIdentityLink(task.getId(), identityLink.getUser(), identityLink.getType());\n        }\n\n        return restResponseFactory.createRestIdentityLink(identityLink.getType(), identityLink.getUser(), identityLink.getGroup(), task.getId(), null, null);\n    }","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/task/TaskIdentityLinkCollectionResource.java#L57-L93","documentation":"REST request validation in TaskIdentityLinkCollectionResource.createIdentityLink: the body supplied both a user and a group for a single identity link; an identity link targets exactly one principal type, so the combination is rejected.","triggerScenarios":"POST /runtime/tasks/{taskId}/identitylinks with a body containing both fields, e.g. {\"type\":\"candidate\",\"user\":\"kermit\",\"group\":\"management\"}.","commonSituations":"UIs that keep the previous selection populated when switching between user/group modes; bulk-merge code that copies all form fields into the payload.","solutions":["Send only one of 'user' or 'group' per request; issue two requests for both","Clear the other field in the form/model before submitting","Split user and group assignments into separate API calls"],"exampleFix":"// before\n{\"type\": \"candidate\", \"user\": \"kermit\", \"group\": \"management\"}\n// after\n{\"type\": \"candidate\", \"user\": \"kermit\"}\n// plus a second call\n{\"type\": \"candidate\", \"group\": \"management\"}","handlingStrategy":"validation","validationCode":"if (payload.user && payload.group) {\n  throw new Error('send only one of user or group per identity link');\n}","typeGuard":null,"tryCatchPattern":"try { ... } catch (e) { if (e.status === 400 && /Only one of user or group/.test(e.body.message)) { splitIntoTwoRequests(); } else { throw e; } }","preventionTips":["Clear the unused field when switching between user/group modes","Issue separate requests for user and group links","Never copy all form fields blindly into the payload"],"tags":["rest","validation","flowable","identity-links"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T16:30:33.424Z"}