{"record":{"id":"fb4982d93ef802b7","repo":"flowable/flowable-engine","slug":"job-with-id-jobid-does-not-have-an-exception","errorCode":null,"errorMessage":"Job with id '${jobId}' does not have an exception stacktrace.","messagePattern":"Job with id '(.+?)' does not have an exception stacktrace\\.","errorType":"http","errorClass":"FlowableObjectNotFoundException","httpStatus":404,"severity":"info","filePath":"modules/flowable-rest/src/main/java/org/flowable/rest/service/api/management/JobExceptionStacktraceResource.java","lineNumber":51,"sourceCode":" * @author Joram Barrez\n */\n@RestController\n@Api(tags = { \"Jobs\" }, authorizations = { @Authorization(value = \"basicAuth\") })\npublic class JobExceptionStacktraceResource extends JobBaseResource {\n\n    @ApiOperation(value = \"Get the exception stacktrace for a job\", tags = { \"Jobs\" })\n    @ApiResponses(value = {\n            @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.\"),\n            @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.\")\n    })\n    @GetMapping(\"/management/jobs/{jobId}/exception-stacktrace\")\n    public String getJobStacktrace(@ApiParam(name = \"jobId\") @PathVariable String jobId, HttpServletResponse response) {\n        Job job = getJobById(jobId);\n\n        String stackTrace = managementService.getJobExceptionStacktrace(job.getId());\n\n        if (stackTrace == null) {\n            throw new FlowableObjectNotFoundException(\"Job with id '\" + job.getId() + \"' does not have an exception stacktrace.\", String.class);\n        }\n\n        response.setContentType(\"text/plain\");\n        return stackTrace;\n    }\n\n    @ApiOperation(value = \"Get the exception stacktrace for a timer job\", tags = { \"Jobs\" })\n    @ApiResponses(value = {\n            @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.\"),\n            @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.\")\n    })\n    @GetMapping(\"/management/timer-jobs/{jobId}/exception-stacktrace\")\n    public String getTimerJobStacktrace(@ApiParam(name = \"jobId\") @PathVariable String jobId, HttpServletResponse response) {\n        Job job = getTimerJobById(jobId);\n\n        String stackTrace = managementService.getTimerJobExceptionStacktrace(job.getId());\n\n        if (stackTrace == null) {","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/management/JobExceptionStacktraceResource.java#L33-L69","documentation":"getJobStacktrace fetches a job and then its stored exception stacktrace; when managementService.getJobExceptionStacktrace returns null (the job exists but never failed, so no stacktrace was recorded), a FlowableObjectNotFoundException (String.class) is thrown.","triggerScenarios":"GET /management/jobs/{jobId}/exception-stacktrace for a job that has not yet thrown an exception (e.g. a healthy waiting timer or a freshly created job).","commonSituations":"Monitoring scripts that poll stacktraces for every job, jobs retried successfully so the stacktrace column was cleared, or fetching before the first failed execution.","solutions":["Only request the stacktrace for jobs whose retries were decremented / that are in failed state.","Treat a 404 here as 'no failure recorded yet' rather than a missing job.","Check the job's exception message first via the job resource before fetching the stacktrace.","Wait for the next failure if the job is expected to fail but has not executed yet."],"exampleFix":"// before\ncurl http://localhost:8080/flowable-rest/management/jobs/7/exception-stacktrace  // 404\n// after: verify the job actually failed first\ncurl http://localhost:8080/flowable-rest/management/jobs/7 | jq '.exceptionMessage'\n# only fetch stacktrace when exceptionMessage is non-null","handlingStrategy":"try-catch","validationCode":"const job = await fetch(`${base}/management/jobs/${jobId}`).then(r => r.json());\nif (!job.exceptionMessage) return null; // no stacktrace exists yet","typeGuard":"function hasRecordedFailure(job) { return !!job && typeof job.exceptionMessage === 'string' && job.exceptionMessage.length > 0; }","tryCatchPattern":"try {\n  const trace = await getJobStacktrace(jobId);\n} catch (e) {\n  if (e.status === 404) return null; // job has no failure recorded\n  throw e;\n}","preventionTips":["Only fetch stacktraces for jobs reporting a non-null exceptionMessage.","Interpret 404 on stacktrace endpoints as 'no failure yet', not 'job missing'.","For repeatedly failing jobs check deadletter-jobs, which keeps the final stacktrace."],"tags":["rest-api","job","stacktrace","not-found"],"backgroundTag":"empty-result-set","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}