flowable/flowable-engine · info · FlowableObjectNotFoundException

Timer job with id '${jobId}' does not have an exception stac

Error message

Timer job with id '${jobId}' does not have an exception stacktrace.

What it means

Same guard as the plain-job variant but for timer jobs: getTimerJobStacktrace throws FlowableObjectNotFoundException when managementService.getTimerJobExceptionStacktrace returns null, i.e. the timer job has no recorded exception.

Source

Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/management/JobExceptionStacktraceResource.java:70

        }

        response.setContentType("text/plain");
        return stackTrace;
    }

    @ApiOperation(value = "Get the exception stacktrace for a timer job", tags = { "Jobs" })
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Indicates the requested job was not found and the stacktrace has been returned. The response contains the raw stacktrace and always has a Content-type of text/plain."),
            @ApiResponse(code = 404, message = "Indicates the requested job was not found or the job does not have an exception stacktrace. Status-description contains additional information about the error.")
    })
    @GetMapping("/management/timer-jobs/{jobId}/exception-stacktrace")
    public String getTimerJobStacktrace(@ApiParam(name = "jobId") @PathVariable String jobId, HttpServletResponse response) {
        Job job = getTimerJobById(jobId);

        String stackTrace = managementService.getTimerJobExceptionStacktrace(job.getId());

        if (stackTrace == null) {
            throw new FlowableObjectNotFoundException("Timer job with id '" + job.getId() + "' does not have an exception stacktrace.", String.class);
        }

        response.setContentType("text/plain");
        return stackTrace;
    }

    @ApiOperation(value = "Get the exception stacktrace for a suspended job", tags = { "Jobs" })
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Indicates the requested job was not found and the stacktrace has been returned. The response contains the raw stacktrace and always has a Content-type of text/plain."),
            @ApiResponse(code = 404, message = "Indicates the requested job was not found or the job does not have an exception stacktrace. Status-description contains additional information about the error.")
    })
    @GetMapping("/management/suspended-jobs/{jobId}/exception-stacktrace")
    public String getSuspendedJobStacktrace(@ApiParam(name = "jobId") @PathVariable String jobId, HttpServletResponse response) {
        Job job = getSuspendedJobById(jobId);

        String stackTrace = managementService.getSuspendedJobExceptionStacktrace(job.getId());

        if (stackTrace == null) {

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Request stacktraces only for timer jobs that show a non-null exceptionMessage.
  2. For failed timer jobs, look in /management/deadletter-jobs/{id}/exception-stacktrace instead.
  3. Handle 404 as 'job healthy / no stacktrace' in monitoring code.
  4. Re-query the timer job to confirm it still exists after rescheduling.

Example fix

// before
curl http://localhost:8080/flowable-rest/management/timer-jobs/3/exception-stacktrace
// after: check failure state first
JOB=$(curl http://localhost:8080/flowable-rest/management/timer-jobs/3)
echo $JOB | jq -r '.exceptionMessage // empty' | grep -q . && \
  curl http://localhost:8080/flowable-rest/management/timer-jobs/3/exception-stacktrace
Defensive patterns

Strategy: try-catch

Validate before calling

const job = await fetch(`${base}/management/timer-jobs/${jobId}`).then(r => r.json());
if (!job.exceptionMessage) return null; // healthy timer job

Type guard

function hasRecordedFailure(job) { return !!job && typeof job.exceptionMessage === 'string' && job.exceptionMessage.length > 0; }

Try / catch

try {
  const trace = await getTimerJobStacktrace(jobId);
} catch (e) {
  if (e.status === 404) return null;
  throw e;
}

Prevention

When it happens

Trigger: GET /management/timer-jobs/{jobId}/exception-stacktrace for a timer job that has not failed (never executed, or executing successfully).

Common situations: Polling stacktraces for all timer jobs in dashboards, timer jobs that were rescheduled (clearing prior failure data), or confusion between timer-jobs and deadletter-jobs endpoints for failed jobs.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/498e746b3db4ae03. Report an issue: GitHub.