theonedev/onedev · error · ExplicitException
Permission denied calling tool:
Error message
Permission denied calling tool:
What it means
ToolUtils.handleCallException converts any UnauthorizedException found in a tool-call exception chain into an ExplicitException 'Permission denied calling tool: <name>'. This is the AI tool layer's way of surfacing authorization failures from underlying services to the chat user.
Source
Thrown at server-core/src/main/java/io/onedev/server/ai/ToolUtils.java:98
}
return convertToJson(Map.of("successful", true, "filesAndFolders", filesAndFolders));
}
public static JsonNode getToolArguments(ToolExecutionRequest toolExecutionRequest) {
var objectMapper = getObjectMapper();
if (toolExecutionRequest.arguments() == null)
return objectMapper.createObjectNode();
try {
return objectMapper.readTree(toolExecutionRequest.arguments());
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public static void handleCallException(String toolName, Throwable exception) {
if (ExceptionUtils.find(exception, UnauthorizedException.class) != null)
throw new ExplicitException("Permission denied calling tool: " + toolName);
var explicitException = ExceptionUtils.find(exception, ExplicitException.class);
if (explicitException != null)
throw explicitException;
logger.error("Error calling tool: " + toolName, exception);
throw new ExplicitException("Error calling tool '" + toolName + "', check server log for details");
}
public static ChatTool wrapForChat(TaskTool taskTool) {
return new ChatTool() {
@Override
public ToolSpecification getSpecification() {
return taskTool.getSpecification();
}
@Override
public CompletableFuture<ToolExecutionResult> execute(@Nullable IPartialPageRequestHandler handler, Subject subject, JsonNode arguments) {
return CompletableFuture.completedFuture(taskTool.execute(subject, arguments));View on GitHub (pinned to d44925c47c)
Solutions
- Grant the authenticated user the permissions needed for the specific tool's operations in OneDev.
- Run the AI integration under a service account with the appropriate project roles.
- Narrow tool usage to projects the user can access.
Example fix
// before
// user 'bot' has no role on project 'core'
chatClient.call("listIssues", Map.of("project", "core"));
// after
// add 'bot' as a member (e.g. Read role) of project 'core', then
chatClient.call("listIssues", Map.of("project", "core")); Defensive patterns
Strategy: try-catch
Validate before calling
// verify user's project access before invoking the tool
const perms = await getProjectPermissions(user, projectPath);
if (!perms.canRead) throw new Error("User lacks access to project " + projectPath); Try / catch
try { return callTool(name, args); } catch (e) { if (/Permission denied calling tool/.test(e.message)) { await requestAccessOrNotifyUser(name); return null; } throw e; } Prevention
- Run AI integrations under a service account with least-required roles
- Pre-check permissions for each tool's target project
- Catch ExplicitException in the tool layer to surface friendly messages
When it happens
Trigger: Any AI tool invocation whose execution path throws UnauthorizedException — e.g. the agent's user lacks project access required by the tool (reading issues, triggering builds, accessing code).
Common situations: AI agent operating as a user without membership in the target project; revoked permissions mid-session; tools accessing private projects.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- No permission to access build: ${referenceString}
- Access denied
- Tool not found: ${toolName}
- Timed out calling tool: ${toolName}
- Issue schedule permission required to set own estimated time
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/79567487191dcf8b.
Report an issue: GitHub.