theonedev/onedev · error · RefUpdateException

RefUpdateException

Error message

RefUpdateException

What it means

GitUtils.updateRef (public static) wraps JGit RefUpdate.forceUpdate(). After the update it checks the RefUpdate.Result: if the lock failed because the ref moved since the expected old object id was set, it throws ObsoleteCommitException; any other result that is not FAST_FORWARD, FORCED, NEW or NO_CHANGE throws RefUpdateException carrying the JGit result name (e.g. REJECTED, IO_FAILURE). OneDev throws this because the ref update was not applied.

Source

Thrown at server-core/src/main/java/io/onedev/server/git/GitUtils.java:687

	}

	public static RefUpdate getRefUpdate(Repository repository, String refName) {
		try {
			return repository.updateRef(refName);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	public static void updateRef(RefUpdate refUpdate) {
		try {
			RefUpdate.Result result = refUpdate.forceUpdate();
			if (result == RefUpdate.Result.LOCK_FAILURE && refUpdate.getExpectedOldObjectId() != null
					&& !refUpdate.getExpectedOldObjectId().equals(refUpdate.getOldObjectId())) {
				throw new ObsoleteCommitException(refUpdate.getOldObjectId());
			} else if (result != RefUpdate.Result.FAST_FORWARD && result != RefUpdate.Result.FORCED
					&& result != RefUpdate.Result.NEW && result != RefUpdate.Result.NO_CHANGE) {
				throw new RefUpdateException(result);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	public static void deleteRef(RefUpdate refUpdate) {
		try {
			refUpdate.setForceUpdate(true);
			RefUpdate.Result result = refUpdate.delete();
			if (result == RefUpdate.Result.LOCK_FAILURE && refUpdate.getExpectedOldObjectId() != null
					&& !refUpdate.getExpectedOldObjectId().equals(refUpdate.getOldObjectId())) {
				throw new ObsoleteCommitException(refUpdate.getOldObjectId());
			} else if (result != RefUpdate.Result.FAST_FORWARD && result != RefUpdate.Result.FORCED
					&& result != RefUpdate.Result.NEW && result != RefUpdate.Result.NO_CHANGE) {
				throw new RefUpdateException(result);
			}
		} catch (IOException e) {

View on GitHub (pinned to d44925c47c)

Solutions

  1. Read RefUpdateException.getResult() and map the JGit result (REJECTED*, IO_FAILURE) to a concrete cause
  2. Re-fetch the ref, rebuild the RefUpdate with the latest old object id, and retry (handle ObsoleteCommitException for the racing case)
  3. If REJECTED_CURRENT_BRANCH, switch the update to a non-checked-out ref or update the target branch instead
  4. Check filesystem permissions/disk for IO_FAILURE results

Example fix

// before
RefUpdate ru = repo.updateRef("refs/heads/main");
ru.setExpectedOldObjectId(oldId);
ru.setNewObjectId(newId);
GitUtils.updateRef(ru); // throws RefUpdateException: REJECTED
// after
ru.setExpectedOldObjectId(repo.exactRef("refs/heads/main").getObjectId()); // re-read latest
GitUtils.updateRef(ru);
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check before update
Ref ref = repo.exactRef(refName);
ru.setExpectedOldObjectId(ref != null ? ref.getObjectId() : null);
ru.setForceUpdate(true);

Type guard

boolean isUpdatable(RefUpdate.Result r) {
  return r == RefUpdate.Result.FAST_FORWARD || r == RefUpdate.Result.FORCED
      || r == RefUpdate.Result.NEW || r == RefUpdate.Result.NO_CHANGE;
}

Try / catch

try {
  GitUtils.updateRef(ru);
} catch (ObsoleteCommitException e) {
  // re-read ref and retry
} catch (RefUpdateException e) {
  switch (e.getResult()) {
    case REJECTED_CURRENT_BRANCH: /* switch branch/bare repo */ break;
    case IO_FAILURE: /* check permissions/disk */ break;
    default: /* map result to user message */ }
}

Prevention

When it happens

Trigger: Calling GitUtils.updateRef(refUpdate) when forceUpdate() returns a result such as REJECTED_NON_FAST_FORWARD / REJECTED_CURRENT_BRANCH / REJECTED / IO_FAILURE, i.e. the ref update could not be forced. Typical: trying to update HEAD of a branch with concurrent pushes, updating a ref that is currently checked out, or underlying filesystem/refdb IO failure.

Common situations: Push rejected because branch changed concurrently; attempting to overwrite a protected branch; a hook or another process holding the ref; disk/permissions problems in .git producing IO_FAILURE.

Understand the failure class

Background: "git command failed": what it means when a tool shells out to git and git exits non-zero — this error's family across 21 libraries.

Related errors


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