createIncident from serviceTask fails with boundary error

Good Afernoon,

I tried to reproduce this behavior with the sample project I attached to this issue. The process is like this:

image

You will see that tskServiceTask has asyncBefore = true, because I don’t want it to continue if there is a technical error, in this case I want it to avoid retries (R0/PT5M) and stop it at the beginning of tskServiceTask until I decide to resolve the incident I created.

Inside you’ll find a SimpleTestCase and MyTaskDelegate. Inside this delegate I simulate a non-business error (i.e. a technical error) and then I catch it and create an incident. I’ve tried two ways to create it:

1.- creating an IncidentContext and creating it…
IncidentContext context = new IncidentContext();
context.setActivityId(execution.getCurrentActivityId());
context.setExecutionId(execution.getProcessInstanceId());
context.setProcessDefinitionId(execution.getProcessDefinitionId());

        StringWriter sw = new StringWriter();
        e.printStackTrace(new PrintWriter(sw));

        IncidentEntity newIncident
            = IncidentEntity.createAndInsertIncident(
            IncidentEntity.FAILED_JOB_HANDLER_TYPE,
            context,
            sw.toString()
        );

This leads to the following error (I trimmed it since it’s too long):
INFO: ENGINE-14018 JobExecutor[org.camunda.bpm.engine.impl.jobexecutor.DefaultJobExecutor] starting to acquire jobs
oct 31, 2018 5:24:34 PM org.slf4j.impl.JCLLoggerAdapter error
SEVERE: ENGINE-16004 Exception while closing command context: ENGINE-03083 Exception while executing Batch Database Operations with message ’

Error flushing statements. Cause: org.apache.ibatis.executor.BatchExecutorException: org.camunda.bpm.engine.impl.persistence.entity.HistoricIncidentEntity.insertHistoricIncidentEvent (batch index #2) failed. 1 prior sub executor(s) completed successfully, but will be rolled back. Cause: org.h2.jdbc.JdbcBatchUpdateException: Value too long for column “INCIDENT_MSG_ VARCHAR(4000)”: “STRINGDECODE('java.lang.ArithmeticException: / by zero\r\n\tat org.camunda.bpm.unittest.MyTaskDelegate.execute(MyTaskDelegate.ja… (11070)”; SQL statement:

insert into ACT_HI_INCIDENT (
ID_,
PROC_DEF_KEY_,
PROC_DEF_ID_,

2.- This single line to create an incident:
Incident incident = ProcessEngineTests.runtimeService().createIncident(“foo”, execution.getProcessInstanceId(), “tskServiceTask”, e.getMessage());

In this case I get the following error:
org.camunda.bpm.engine.BadUserRequestException: Execution must be related to an activity: activity is null
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)

How can I get the issue created the right way, without these two additional errors, and at the same time get the process flow stuck at the beginning of tskServiceTask until I decide to resolve that incident?

At the incident I’d like to appear as stacktrace just the error that caused it (/ by zero).

Thanks in advance,
Best regards
Alfonso.testProcess.bpmn (5.8 KB)

MyTaskDelegate.java
@Named(“myTaskDelegate”)
public class MyTaskDelegate implements JavaDelegate {
@Override
public void execute(DelegateExecution execution)
throws Exception {

    try {
        // simulate technical error
        int i = 1 / 0;

    } catch (Exception e) {

        boolean createIncidentTheSimpleWay = true;
        if (createIncidentTheSimpleWay) {
            Incident incident = ProcessEngineTests.runtimeService().createIncident("foo", execution.getProcessInstanceId(), "tskServiceTask", e.getMessage());
            int n = 0; // Just to stop breakpoint and inspect incident

        } else {
            IncidentContext context = new IncidentContext();
            context.setActivityId(execution.getCurrentActivityId());
            context.setExecutionId(execution.getProcessInstanceId());
            context.setProcessDefinitionId(execution.getProcessDefinitionId());

            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw));

            IncidentEntity newIncident
                = IncidentEntity.createAndInsertIncident(
                IncidentEntity.FAILED_JOB_HANDLER_TYPE,
                context,
                sw.toString()
            );
        }
    }

}

}

SimpleTestCase.java
public class SimpleTestCase {

@Rule
public ProcessEngineRule rule = new ProcessEngineRule();

//   @InjectMocks
//   private final MyTaskDelegate myTaskDelegate = new MyTaskDelegate();

@Mock(name = "execution")
private DelegateExecution execution;

@Test
@Deployment(resources = {"testProcess.bpmn"})
public void shouldExecuteProcess()
throws Exception {

    MockitoAnnotations.initMocks(this);
    //       Mockito.when(execution.getCurrentActivityId()).thenReturn("tskServiceTask");
    MyTaskDelegate myTaskDelegate1 = new MyTaskDelegate();

    Mocks.register("myTaskDelegate", myTaskDelegate1);
    // Given we create a new process instance
    ProcessInstance processInstance = runtimeService().startProcessInstanceByKey("testProcess");

    /*
    Incident incident = runtimeService().createIncident("foo", processInstance.getId(), "userTask1", "bar");

    // when
    runtimeService().resolveIncident(incident.getId());
    */


    // then
    Incident incident2 = runtimeService().createIncidentQuery().executionId(processInstance.getId()).singleResult();
    assertNull(incident2);

    // Then it should be active
    assertThat(processInstance).isActive();
    // And it should be the only instance
    assertThat(processInstanceQuery().count()).isEqualTo(1);
    // And there should exist just a single task within that process instance
    // assertThat(task(processInstance)).isNotNull();

    // When we complete that task
    //complete(task(processInstance));
    // Then the process instance should be ended

    //assertThat(processInstance).isEnded();
}

}

Hi @alfmateos,

Why do you try to create the incident by hand instead of just relying on the standard job failure mechanism? When the job fails and there are no retries, the process engine creates an incident automatically and the stacktrace is stored along with it.

Cheers,
Thorben

Thanks a lot, Thorben, now I get my incident automatically created with the stack trace I wanted.
But I still get the incident message as null… Is there a way to set that message to whatever I want? (for instance using the exception message as a value for this incident message).

Since the incident is created automatically I don’t see the way to set that message value manually or in another way… is it possible somehow?

Thanks again for your response,
Best Regards,
Alfonso.

Hi Thorben,

When I rely on the standard job failure mechanism, how can I set the incident message to something meaningful? Now it shows as null:

Thanks in advance,
Alfonso

Hi Alfonso,

I think the exception message becomes the message of the incident.

Cheers,
Thorben

Hi Thorben,

The exception message does not seem to become the message of the incident when using the standard job failure mechanism…

Alternatively, can you please tell me how canI get the stack trace for a given incident? I know how to recover them for a given process Instance:

public List<IncidentDtoBasic> getIncidents(String processInstanceId) {
List<Incident> incidents = runtimeService.createIncidentQuery().processInstanceId(processInstanceId).list();

List<IncidentDtoBasic> incidentDtoBasicList = new ArrayList<>();
for (Incident incident : incidents) {
IncidentDtoBasic incidentDto = new IncidentDtoBasic();
dozerBeanMapper.map(incident, incidentDto);
incidentDtoBasicList.add(incidentDto);

}
return incidentDtoBasicList;
}

I saw this:

But I don’t know how to get that job id and how to get the stack trace via the java API…
could you please help me with this?

I don’t care not being able to get the error message if I can get the stack trace…

Thanks a lot in advance,
Alfonso.

Hi Alfonso,

You can obtain the incident’s execution and job definition id via Incident#getExecutionId and Incident#getJobDefinitionId. Then, query for jobs by both of these properties. There should be exactly one resulting job. Then call ManagementService#getJobExceptionStacktrace.

Cheers,
Thorben