theonedev/onedev · error · NotAcceptableException

Tag '${request.getTagName()}' already exists

Error message

Tag '${request.getTagName()}' already exists

What it means

REST createTag checks project.getTagRef before creating and throws this conflict when a tag with the requested name already exists in the repository; permission to create that specific tag is verified first.

Source

Thrown at server-core/src/main/java/io/onedev/server/rest/resource/RepositoryResource.java:236

		RefResponse response = new RefResponse();
		
		response.refName = ref.getName();
		response.commitHash = project.getRevCommit(ref.getObjectId(), true).getName();
		
		return response;
	}
	
	@Api(order=70, description="Create a new tag")
	@Path("/{projectId}/tags")
	@POST
	public Response createTag(@PathParam("projectId") Long projectId, @NotNull CreateTagRequest request) {
		Project project = projectService.load(projectId);
		if (!SecurityUtils.canCreateTag(project, request.getTagName())) {
			throw new UnauthorizedException();
		}

		if (project.getTagRef(request.getTagName()) != null) {
			throw new NotAcceptableException("Tag '" + request.getTagName() + "' already exists");
		} else {
			User user = SecurityUtils.getUser();
			gitService.createTag(project, request.getTagName(), request.getRevision(), user.asPerson(), 
					request.getTagMessage(), project.isTagSignatureRequired(user, request.getTagName()));
		}

		return Response.ok().build();
	}
	
	@Api(order=80, description="Delete specified tag")
	@Path("/{projectId}/tags/{tag:.*}")
	@DELETE
	public Response deleteTag(
			@PathParam("projectId") Long projectId, 
			@PathParam("tag") @Api(example="test-tag") String tagName) {
		Project project = projectService.load(projectId);
		if (!SecurityUtils.canDeleteTag(project, tagName)) {
			throw new UnauthorizedException();

View on GitHub (pinned to d44925c47c)

Solutions

  1. Use a new version/tag name
  2. Delete the existing tag first (if permitted) then recreate it
  3. Make automation idempotent: check GET /~api/{projectId}/tags/{tag} before POST and skip if 200
  4. Fix version numbering scheme to avoid duplicate releases

Example fix

// before
POST /~api/1/tags {"tagName":"v1.0","revision":"main"} // already exists
// after
POST /~api/1/tags {"tagName":"v1.0.1","revision":"main"}
Defensive patterns

Strategy: validation

Validate before calling

let exists = false;
try { await api.get(`/projects/${id}/tags/${tagName}`); exists = true; } catch (e) {}
if (exists) throw new Error(`tag ${tagName} already exists`);

Try / catch

try {
  createTag(projectId, req);
} catch (NotAcceptableException e) {
  // treat as idempotent or bump version
}

Prevention

When it happens

Trigger: Calling POST /~api/{projectId}/tags with a tagName that already resolves to an existing ref (e.g. re-running a release job for an already-cut version).

Common situations: Re-running a failed release pipeline that already created the tag; re-releasing the same version number; collision between automated and manual tagging.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/2160a45e2fa34de3. Report an issue: GitHub.