AlistGo/alist · error

permission denied

Error message

permission denied

What it means

Returned by checkAccess (server/mcp/auth.go:127) when common.CanAccessWithRoles rejects the user for reqPath based on the nearest meta's access rules. This is the read-level path gate evaluated before any MCP permission bit: the user's roles must satisfy the meta visibility and role restrictions attached to the path or a parent directory.

Source

Thrown at server/mcp/auth.go:127

}

// buildFsContext resolves path and sets meta in context for fs operations.
func buildFsContext(ctx context.Context, user *model.User, path string) (context.Context, string, error) {
	reqPath, err := user.JoinPath(path)
	if err != nil {
		return ctx, "", err
	}
	meta, _ := op.GetNearestMeta(reqPath)
	ctx = context.WithValue(ctx, "meta", meta)
	ctx = context.WithValue(ctx, "user", user)
	return ctx, reqPath, nil
}

// checkAccess checks if user can access the path (read).
func checkAccess(user *model.User, reqPath string) error {
	meta, _ := op.GetNearestMeta(reqPath)
	if !common.CanAccessWithRoles(user, meta, reqPath, "") {
		return fmt.Errorf("permission denied")
	}
	perm := common.MergeRolePermissions(user, reqPath)
	if !user.IsAdmin() && !common.HasPermission(perm, common.PermMCPAccess) {
		return fmt.Errorf("MCP access not permitted")
	}
	return nil
}

// checkManage checks if user can perform write operations via MCP.
func checkManage(user *model.User, reqPath string, permBit uint) error {
	if err := checkAccess(user, reqPath); err != nil {
		return err
	}
	perm := common.MergeRolePermissions(user, reqPath)
	if !user.IsAdmin() && !common.HasPermission(perm, common.PermMCPManage) {
		return fmt.Errorf("MCP manage not permitted")
	}
	if !user.IsAdmin() && !common.HasPermission(perm, permBit) {

View on GitHub (pinned to 843d9dc814)

Solutions

  1. Check that the user's role base path covers the requested path
  2. Inspect the nearest meta on the path and relax its visibility or role list if appropriate
  3. Switch to an account whose roles cover the path
  4. Remember the check walks up: a meta on any ancestor applies
Defensive patterns

Strategy: validation

Validate before calling

// before the call, confirm the path is inside one of the user's role base paths
func covered(userRoles []Role, p string) bool {
  for _, r := range userRoles { if isSub(r.BasePath, p) { return true } }
  return false
}

Prevention

When it happens

Trigger: A non-admin user whose role scope (base path) does not cover reqPath; a directory meta marked hidden or restricted to other roles; the guest account hitting a path with a restrictive meta.

Common situations: Roles scoped to /data/team-a while the tool requests /data/team-b; metas set to private on upload roots; newly added meta restrictions breaking previously working MCP flows.

Related errors


AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15). Data as JSON: /api/errors/a226a24f211a36ba. Report an issue: GitHub.