{"record":{"id":"f948b36c92091db3","repo":"theonedev/onedev","slug":"reason-is-required","errorCode":null,"errorMessage":"Reason is required","messagePattern":"Reason is required","errorType":"exception","errorClass":"io.onedev.server.exception.NotAcceptableException","httpStatus":406,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/ai/TodResource.java","lineNumber":1735,"sourceCode":"            params = new HashMap<String, List<String>>();\n            List<String> paramPairs = (List<String>) paramData;\n            if (paramPairs != null) {\n                for (var paramPair: paramPairs) {\n                    var paramName = trimToNull(StringUtils.substringBefore(paramPair, \"=\"));\n                    var paramValue = trimToNull(StringUtils.substringAfter(paramPair, \"=\"));\n                    if (paramName != null && paramValue != null)\n                        params.computeIfAbsent(paramName, k -> new ArrayList<>()).add(paramValue);\n                }\n            }\n        } else if (paramData instanceof Map) {\n            params = (Map<String, List<String>>) paramData;\n        } else {\n            params = new HashMap<String, List<String>>();\n        }\n\n        var reason = trimToNull((String)data.get(\"reason\"));\n        if (reason == null)\n            throw new NotAcceptableException(\"Reason is required\");\n            \n        var build = jobService.submit(user, project, ObjectId.fromString(commitHash), jobName, \n            params, refName, null, null, reason);\n        if (build.isFinished())\n            jobService.resubmit(user, build, reason);\n\n        var summary = BuildHelper.getSummary(project, build);\n        summary.put(\"id\", build.getId());\n        return summary;\n    }\n\n    @Path(\"/get-clone-roots\")\n    @GET\n    public Map<String, String> getCloneRoots() {\n        if (SecurityUtils.getUser() == null)\n            throw new UnauthenticatedException();\n\n        var cloneRoots = new HashMap<String, String>();","sourceCodeStart":1717,"sourceCodeEnd":1753,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/ai/TodResource.java#L1717-L1753","documentation":"When submitting a job through TodResource, a human-readable 'reason' string is mandatory; it is recorded as the audit context passed to jobService.submit (and resubmit). A null/blank reason causes NotAcceptableException before any build is created.","triggerScenarios":"Calling the job-submit tool endpoint with a valid commitHash/branch/tag and params, but data.get(\"reason\") null or empty.","commonSituations":"AI agents or scripts automating build submissions that treat 'reason' as optional metadata; refactors of tool payloads that drop the reason field.","solutions":["Set data.put(\"reason\", \"<why this build is being submitted>\") before calling the endpoint.","Generate a default reason programmatically if none is available (e.g. 'triggered by AI agent').","Audit your tool-calling layer to always populate the reason field."],"exampleFix":"// before\nvar data = Map.of(\"branch\", \"main\", \"jobName\", \"ci\");\n// after\nvar data = Map.of(\"branch\", \"main\", \"jobName\", \"ci\", \"reason\", \"manual rebuild requested by user\");","handlingStrategy":"validation","validationCode":"if (!(data.reason ?? \"\").trim()) throw new Error(\"reason is required for job submission\");","typeGuard":"function hasReason(d) { return typeof d.reason === \"string\" && d.reason.trim().length > 0; }","tryCatchPattern":"try { submit(data); } catch (e) { if (e.status === 406 && e.message.includes(\"Reason is required\")) { data.reason = \"automated submission\"; return submit(data); } throw e; }","preventionTips":["Make 'reason' a mandatory field in your automation's submit template","Auto-generate contextual reasons (user, ticket, trigger source)","Keep audit-friendly reasons even for fully automated flows"],"tags":["missing-required-parameter","rest-api","audit"],"backgroundTag":"missing-required-argument","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}