Why camundaClient.newCompleteUserTaskCommand is requesting /v2/user-tasks/:taskId/completion

I am using camunda-spring-boot-starter.8.9.0-SNAPSHOT
with c8run
and when I try to complete task using:

camundaClient.newCompleteUserTaskCommand

it gives me an error:

2025-09-22T12:52:49.126+03:00 ERROR 5956 --- [camunda-api] [nio-7777-exec-2] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with path [/camunda-api] threw exception [Request processing failed: io.camunda.client.api.command.ProblemException: Failed with code 503: 'Service Unavailable'. Details: 'class ProblemDetail {
    type: about:blank
    title: UNAVAILABLE
    status: 503
    detail: Expected to handle request, but there was a connection error with one of the brokers
    instance: /v2/user-tasks/2251799813691838/completion
}'] with root cause

io.camunda.client.api.command.ProblemException: Failed with code 503: 'Service Unavailable'. Details: 'class ProblemDetail {
    type: about:blank
    title: UNAVAILABLE
    status: 503
    detail: Expected to handle request, but there was a connection error with one of the brokers
    instance: /v2/user-tasks/2251799813691838/completion
}'

The question is why it is calling /v2/user-tasks instead of
/v1/tasks/:taskId/complete

Hi there! :wave:

Great question about the API endpoint behavior. Let me clarify what’s happening here.

Why /v2/user-tasks instead of /v1/tasks?

The camundaClient.newCompleteUserTaskCommand is correctly using the Camunda 8 REST API v2 endpoint (/v2/user-tasks/:taskId/completion). This is the expected behavior for Camunda 8.9, and here’s why:

API Evolution in Camunda 8

  • v2 API (/v2/user-tasks) is the current standard for Camunda/Zeebe user tasks lifecycle operations (assign, unassign, update, complete)
  • v1 API (/v1/tasks) is the legacy Tasklist API that’s been deprecated since 8.8 and is scheduled for removal in 8.10

Key Differences:

  • v2: For Camunda user tasks (modeled as Camunda/Zeebe User Task elements)
  • v1: For legacy job worker-based user tasks

The Real Issue: 503 Service Unavailable

The problem isn’t the endpoint choice - it’s the 503 error indicating a connection issue with the brokers. This typically means:

  1. Gateway-to-Broker routing problem - The gateway can’t deliver the request to a broker/partition
  2. Partition awareness issue - The gateway isn’t aware of the partition where your task resides
  3. Configuration mismatch - Incorrect addresses or context paths

Troubleshooting Steps:

1. Check Gateway/Broker Health

# Check logs for partition/connection errors
docker logs <zeebe-broker>
docker logs <zeebe-gateway>

# Health check
curl http://localhost:9600/actuator/health

2. Verify Configuration

Make sure your Spring Boot configuration is correct:

camunda:
  client:
    mode: self-managed
    zeebe:
      rest-address: http://{gateway-host}:{gateway-rest-port}
      # Include context path if configured (e.g., /zeebe)

3. Common Fixes:

  • Restart the gateway if it lost partition awareness
  • Include context path in rest-address if your gateway is configured with one
  • Ensure version compatibility between your client (8.9.0-SNAPSHOT) and cluster
  • Check port configuration - Use gateway’s REST port (typically 8080), not Tasklist port (8082)

References:

The endpoint choice is correct - focus on resolving the broker connectivity issue. Let me know if you need help with any specific configuration details!

@zee-bot even when I call /v1/tasks/:taskId/complete
it gives an exception:

Caused by: java.lang.NullPointerException: Cannot invoke "String.getBytes(java.nio.charset.Charset)" because "value" is null
	at io.camunda.zeebe.util.StringUtil.getBytes(StringUtil.java:39)
	at io.camunda.zeebe.util.StringUtil.getBytes(StringUtil.java:35)
	at io.camunda.zeebe.msgpack.property.StringProperty.setValue(StringProperty.java:38)
	at io.camunda.zeebe.protocol.impl.record.value.usertask.UserTaskRecord.setAssignee(UserTaskRecord.java:533)
	at io.camunda.zeebe.gateway.impl.broker.request.BrokerUserTaskAssignmentRequest.<init>(BrokerUserTaskAssignmentRequest.java:23)
	at io.camunda.service.UserTaskServices.assignUserTask(UserTaskServices.java:145)
	at io.camunda.tasklist.webapp.service.CamundaServicesBasedAdapter.lambda$assignCamundaUserTask$3(CamundaServicesBasedAdapter.java:154)
	at io.camunda.tasklist.webapp.service.CamundaServicesBasedAdapter.executeCamundaService(CamundaServicesBasedAdapter.java:249)

Thanks for the additional details! This NullPointerException reveals the root cause of your issue.

The Real Problem: Null Assignee

The stack trace shows that the system is trying to assign a user task with a null assignee, which causes the NPE:

UserTaskRecord.setAssignee(UserTaskRecord.java:533)
BrokerUserTaskAssignmentRequest.<init>(BrokerUserTaskAssignmentRequest.java:23)

This happens because:

  1. The system is attempting to assign the task (either explicitly or implicitly during completion)
  2. The assignee value is null, which causes StringUtil.getBytes() to fail

Why This Affects Both v1 and v2 APIs

Both API versions ultimately go through the same broker assignment logic, which is why you’re seeing the same NPE regardless of which endpoint you use.

Solutions:

1. Ensure Non-Null Assignee

When completing a user task, make sure it’s properly assigned first:

// First assign the task
camundaClient.newAssignUserTaskCommand(userTaskKey)
    .assignee("your-user-id")  // Must be non-null
    .send();

// Then complete it
camundaClient.newCompleteUserTaskCommand(userTaskKey)
    .send();

2. Check Your Process Model

If you’re using assignee expressions in your BPMN model, ensure they don’t evaluate to null:

// Instead of this (which might be null):
= userId

// Use this with a fallback:
= get or else(userId, "default-user")

// Or with validation:
= assert(userId != null, "Assignee must not be null")

3. Use Candidate Groups Instead

If you don’t have a specific assignee, consider using candidate groups and let users claim tasks:

<bpmn:userTask id="task" name="My Task">
  <bpmn:extensionElements>
    <zeebe:assignmentDefinition candidateGroups="=candidateGroupsList" />
  </bpmn:extensionElements>
</bpmn:userTask>

Potential Bug Report

The NPE suggests this might be a bug - the system should return a proper validation error (400) instead of crashing with NPE. If you can reproduce this consistently, consider filing a bug report at:

Include:

  • Exact API call or client command
  • Camunda version (8.9.0-SNAPSHOT)
  • Environment (c8run)
  • Steps to reproduce

Quick Fix for Testing

To immediately resolve this, ensure your task has a valid assignee before attempting completion. You can check the task’s current state via the query APIs first.

Does your user task have an assignee set, or are you trying to complete an unassigned task? That would help narrow down the exact scenario causing this issue.

Hi @devmsaleh - I would recommend you don’t get so far into the future quite yet. 8.9 is scheduled for 2026, with 8.8 releasing next month. Anything tagged with 8.9.x as the version is going to be HIGHLY unstable at this extremely early stage. I would recommend working against 8.8 until we release an 8.9 alpha candidate in the coming months.

You can learn more about the API changes the bot described in these posts:

Excellent point from the community! :dart:

You’re absolutely right - Camunda 8.9.0-SNAPSHOT is highly unstable since 8.9 isn’t scheduled until 2026. The issues you’re experiencing are likely due to using this very early development version.

Immediate Recommendation

Switch to Camunda 8.8 (releasing next month) or the current stable version. The 8.9.0-SNAPSHOT you’re using is in very early development and not suitable for any real development work.

Why This Matters for Your Issue

The NullPointerException you’re seeing could very well be:

  1. A bug in the unstable 8.9 snapshot
  2. Incomplete implementation in the development version
  3. Breaking changes that aren’t yet documented

Next Steps

  1. Downgrade to a stable version (8.7.x or wait for 8.8)
  2. Update your dependencies:
    <dependency>
        <groupId>io.camunda.spring</groupId>
        <artifactId>camunda-spring-boot-starter</artifactId>
        <version>8.7.x</version> <!-- Use stable version -->
    </dependency>
    
  3. Test your user task completion with the stable version

Learn More About API Changes

The community member shared excellent resources about the API evolution:

Once you’re on a stable version, if you still encounter issues with user task completion, we can help troubleshoot the actual problem rather than development version instabilities.

Thanks for bringing this up - it’s a great reminder about version stability! :pray: