theonedev/onedev · error · NotAcceptableException
No applicable manual transition spec found for current user
Error message
No applicable manual transition spec found for current user (issue: {0}, from state: {1}, to state: {2}) What it means
State transitions via the REST API must match a manual transition spec defined in the project's issue workflow that applies to the current user, current state, and target state. issue.getProject().getManualSpec(subject, issue, toState) returning null yields NotAcceptableException (HTTP 406) with issue, from-state and to-state details.
Source
Thrown at server-core/src/main/java/io/onedev/server/rest/resource/IssueResource.java:474
var example = new LinkedHashMap<String, Serializable>();
example.put("field1", "value1");
example.put("field2", new String[]{"value1", "value2"});
return example;
}
@Api(order=1500)
@Path("/{issueId}/state-transitions")
@POST
public Response transitState(@PathParam("issueId") Long issueId, @NotNull @Valid StateTransitionData data) {
Issue issue = issueService.load(issueId);
var subject = SecurityUtils.getSubject();
var user = SecurityUtils.getUser(subject);
ManualSpec transition = issue.getProject().getManualSpec(subject, issue, data.getState());
if (transition == null) {
var message = MessageFormat.format(
"No applicable manual transition spec found for current user (issue: {0}, from state: {1}, to state: {2})",
issue.getReference().toString(), issue.getState(), data.getState());
throw new NotAcceptableException(message);
}
var fieldValues = FieldUtils.getFieldValues(subject, issue.getProject(), data.getFields());
issueChangeService.changeState(user, issue, data.getState(), fieldValues,
transition.getPromptFields(), transition.getRemoveFields(), data.getComment());
return Response.ok().build();
}
@Api(order=1550, example = "/~downloads/projects/1/attachments/6a5a1a20-c8c0-44a5-a1bb-8a3d2a830094/attachment.txt",
description = "Upload attachment to issue and get attachment url via response. This url can then be used in issue description or comment")
@Path("/{issueId}/attachments/{preferredAttachmentName}")
@POST
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
public String uploadAttachment(@PathParam("issueId") Long issueId, @PathParam("preferredAttachmentName") String preferredAttachmentName, InputStream input) {
Issue issue = issueService.load(issueId);
if (!SecurityUtils.canModifyIssue(issue))
throw new UnauthorizedException();
View on GitHub (pinned to d44925c47c)
Solutions
- Add/adjust a manual transition spec in Project -> Issue Setting -> Workflow for the from-state to the target state applicable to the user.
- Transition through intermediate states that have defined specs, in order.
- Verify the exact target state name matches the workflow state.
- Ensure the API user satisfies the transition spec's authorization/condition.
Example fix
// before
POST /api/issues/12/transit {"state":"Closed"} // no spec Open->Closed
// after: define a manual transition Open -> Closed in the workflow, or
POST /api/issues/12/transit {"state":"In Progress"} // then Closed Defensive patterns
Strategy: validation
Validate before calling
const workflow = await getIssueWorkflow(projectPath);
const spec = workflow.states[state]
?.transitions?.find(t => t.toState === targetState && t.type === 'MANUAL' && appliesToUser(t, me));
if (!spec) throw new Error(`No manual transition ${state} -> ${targetState} for current user; check workflow`); Try / catch
try {
await api.transitIssueState(issueId, payload);
} catch (e) {
if (e.status === 406 && /No applicable manual transition spec/.test(e.message))
log.error(`Workflow has no manual transition to ${payload.state}; adjust workflow or path`);
else throw e;
} Prevention
- Mirror the project's workflow graph in automation and only traverse defined manual transitions.
- Use exact state names as defined in the workflow spec.
- After workflow edits, regenerate the transition map used by scripts.
- Account for role/condition restrictions in transition specs.
When it happens
Trigger: POST to /api/issues/{issueId}/transit (transitState) with data.getState() equal to a state for which no applicable manual transition spec exists for the user — e.g., target state only reachable via other transitions, spec restricted by role/condition, or state name mismatch.
Common situations: Automations skipping multi-step workflows (attempting direct jump to a closed state); workflow customized so transitions require authorization the API user lacks; renamed states causing exact-name mismatches.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- ${e.getMessage()}
- At least one email address should be present for a user
- Time tracking needs to be enabled for the project
- Iteration is not defined in project hierarchy of the issue
- Count should not be greater than ${MAX_COMMITS}
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/6ddb42d108188238.
Report an issue: GitHub.