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
- Read RefUpdateException.getResult() and map the JGit result (REJECTED*, IO_FAILURE) to a concrete cause
- Re-fetch the ref, rebuild the RefUpdate with the latest old object id, and retry (handle ObsoleteCommitException for the racing case)
- If REJECTED_CURRENT_BRANCH, switch the update to a non-checked-out ref or update the target branch instead
- 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
- Always set expectedOldObjectId from a freshly read ref to get ObsoleteCommitException instead of silent races
- Serialize ref mutations per ref (locks/queues) to avoid concurrent pushes
- Check result name in RefUpdateException.getResult() before retrying
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
- ObsoleteCommitException
- %s (project id: %d, ref: %s)
- Ref name is required when commit hash is specified
- Either commit hash, branch or tag should be specified
- Unable to find commit to import build spec (import project:
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/45d67893eb596e44.
Report an issue: GitHub.