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

  1. Grant the authenticated user the permissions needed for the specific tool's operations in OneDev.
  2. Run the AI integration under a service account with the appropriate project roles.
  3. 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

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


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