{"record":{"id":"32c0e30f976dca0f","repo":"flowable/flowable-engine","slug":"tenantid-can-only-be-used-with-either-processdefin","errorCode":null,"errorMessage":"TenantId can only be used with either processDefinitionKey or message.","messagePattern":"TenantId can only be used with either processDefinitionKey or message\\.","errorType":"validation","errorClass":"FlowableIllegalArgumentException","httpStatus":400,"severity":"error","filePath":"modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/ProcessInstanceCollectionResource.java","lineNumber":343,"sourceCode":"    })\n    @PostMapping(value = \"/runtime/process-instances\", produces = \"application/json\")\n    @ResponseStatus(HttpStatus.CREATED)\n    public ProcessInstanceResponse createProcessInstance(@RequestBody ProcessInstanceCreateRequest request) {\n\n        if (request.getProcessDefinitionId() == null && request.getProcessDefinitionKey() == null && request.getMessage() == null) {\n            throw new FlowableIllegalArgumentException(\"Either processDefinitionId, processDefinitionKey or message is required.\");\n        }\n\n        int paramsSet = ((request.getProcessDefinitionId() != null) ? 1 : 0) + ((request.getProcessDefinitionKey() != null) ? 1 : 0) + ((request.getMessage() != null) ? 1 : 0);\n\n        if (paramsSet > 1) {\n            throw new FlowableIllegalArgumentException(\"Only one of processDefinitionId, processDefinitionKey or message should be set.\");\n        }\n\n        if (request.isTenantSet()) {\n            // Tenant-id can only be used with either key or message\n            if (request.getProcessDefinitionId() != null) {\n                throw new FlowableIllegalArgumentException(\"TenantId can only be used with either processDefinitionKey or message.\");\n            }\n        }\n        \n        Map<String, Object> startVariables = null;\n        Map<String, Object> transientVariables = null;\n        Map<String, Object> startFormVariables = null;\n        if (request.getStartFormVariables() != null && !request.getStartFormVariables().isEmpty()) {\n            startFormVariables = new HashMap<>();\n            for (RestVariable variable : request.getStartFormVariables()) {\n                if (variable.getName() == null) {\n                    throw new FlowableIllegalArgumentException(\"Variable name is required.\");\n                }\n                startFormVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));\n            }\n        }\n\n        if (request.getVariables() != null && !request.getVariables().isEmpty()) {\n            startVariables = new HashMap<>();","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/ProcessInstanceCollectionResource.java#L325-L361","documentation":"FlowableIllegalArgumentException thrown by createProcessInstance when the request sets a tenantId (request.isTenantSet() is true) together with processDefinitionId. Tenant resolution happens when looking up a definition by key or message; with an explicit definition id the tenant is already fully determined, so passing a tenantId is contradictory and rejected.","triggerScenarios":"POST /runtime/process-instances with both processDefinitionId and tenantId (or the tenantId field populated such that isTenantSet() returns true) in the body.","commonSituations":"Multi-tenant clients always including tenantId out of habit, generic request builders that attach tenant context to every start call, migrating code from key-based to id-based starts while keeping the tenant field.","solutions":["Remove tenantId from the request when starting by processDefinitionId","Switch to processDefinitionKey plus tenantId if tenant scoping is required","Have the server-side tenant context resolve the definition key per tenant instead of sending an id","Audit request-building code to only add tenantId for key/message starts"],"exampleFix":"// before\n{\"processDefinitionId\":\"orderProcess:2:125\",\"tenantId\":\"acme\"}\n// after\n{\"processDefinitionKey\":\"orderProcess\",\"tenantId\":\"acme\"}","handlingStrategy":"validation","validationCode":"if (req.tenantId != null && req.processDefinitionId != null) {\n  throw new Error('tenantId cannot be combined with processDefinitionId; use processDefinitionKey or message');\n}","typeGuard":"function tenantUsageValid(req) {\n  return !(req?.tenantId != null && req?.processDefinitionId != null);\n}","tryCatchPattern":"try { await startProcessInstance(req); } catch (e) { if (e.status === 400 && /TenantId can only be used/.test(e.message)) { delete req.processDefinitionId; } throw e; }","preventionTips":["Only attach tenantId when starting by key or message","Remove tenant fields when switching to definition-id based starts","Centralize start-request construction so tenant rules live in one place"],"tags":["rest","validation","tenant","flowable"],"backgroundTag":"conflicting-config-options","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}