theonedev/onedev · error · IncorrectCredentialsException
Invalid or expired access token
Error message
Invalid or expired access token
What it means
BearerAuthenticationFilter authenticates requests carrying a Bearer token. If the token matches no access token, agent token, or active job context, it throws IncorrectCredentialsException('Invalid or expired access token'), which Shiro treats as failed authentication.
Source
Thrown at server-core/src/main/java/io/onedev/server/security/BearerAuthenticationFilter.java:66
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated()) {
String bearerToken = SecurityUtils.getBearerToken((HttpServletRequest)request);
if (bearerToken != null) {
if (clusterService.getCredential().equals(bearerToken)) {
ThreadContext.bind(userService.getSystem().asSubject());
} else {
var accessToken = accessTokenService.findByValue(bearerToken);
if (accessToken != null) {
ThreadContext.bind(accessToken.asSubject());
} else {
var workspaceContext = workspaceService.getWorkspaceContext(bearerToken, false);
if (workspaceContext != null) {
var workspace = workspaceService.load(workspaceContext.getWorkspaceId());
ThreadContext.bind(workspace.getUser().asSubject());
ProjectEvent.setContextualParticipatingUserIds(workspace.getParticipatingUserIds());
} else if (agentTokenService.find(bearerToken) == null
&& jobService.getJobContext(bearerToken, false) == null) {
throw new IncorrectCredentialsException("Invalid or expired access token");
}
}
}
}
}
return true;
}
@Override
protected void cleanup(ServletRequest request, ServletResponse response, Exception existing)
throws ServletException, IOException {
try {
super.cleanup(request, response, existing);
} finally {
ProjectEvent.clearContextualParticipatingUserIds();
}
}
View on GitHub (pinned to d44925c47c)
Solutions
- Generate a fresh access token from user profile > Access Tokens and update the client.
- Verify the token belongs to the correct OneDev instance.
- For agents, re-sync the agent token after re-registration.
- Check the Authorization header format is exactly 'Bearer <token>' with no extra whitespace.
Example fix
// before curl -H "Authorization: Bearer old-expired-token" https://onedev/api/projects // after curl -H "Authorization: Bearer <newly-generated-token>" https://onedev/api/projects
Defensive patterns
Strategy: try-catch
Validate before calling
if (bearerToken == null || bearerToken.isBlank()) throw new IllegalArgumentException("Missing bearer token");
// Optionally probe the token against the API before real use Try / catch
try {
// request with Authorization: Bearer <token>
} catch (UnauthenticatedException | IncorrectCredentialsException e) {
// refresh token and retry once
} Prevention
- Rotate and store access tokens securely with expiry tracking
- Re-fetch tokens after revocation, user logout, or job completion
- Verify the 'Bearer ' header prefix and token instance match
When it happens
Trigger: HTTP request with an Authorization: Bearer header whose token is not a valid access token, agent token, or job execution token (e.g. hitting the REST/API endpoint with a stale token).
Common situations: Access token revoked or expired; token rotated in user profile; using an API token from another OneDev instance; agents reinstalled with new tokens; CI job token used after job completion.
Related errors
- Authentication required
- This api can only be accessed via cluster credential
- Not authenticated
- Account is disabled
- Two-factor authentication not enabled
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/7b453dae4dabfeb8.
Report an issue: GitHub.