SharePoint HealthReport - Missing Setup File

Published on Tuesday, 26 January 2021

This is part of a series on SharePoint Health Report issues, because I can never remember what to do about them.

Posts in this series

Missing setup file

The warning looks like this:

Category        : MissingSetupFile
Error           : True
UpgradeBlocking : False
Message         : File [Features\MySolution_Feature1\WebPart\WebPart.webpart]
                  is referenced [100] times in the database [ContentDatabaseName],
                  but is not installed in the current farm. Please install any
                  feature/solution which contains this file.
Remedy          : One or more files are referenced in the database [ContentDatabaseName],
                  but are not installed in the current farm.  Please install any
                  feature or solution which contain these files.

The solution is either to install the missing dependency - as the warning suggests, or to remove the offending reference. To find the offending reference, a query to the database has to be utilized:

select Id, SiteId, DirName, LeafName, WebId, ListId from AllDocs with (NOLOCK)
where SetupPath = 'Features\MySolution_Feature1\WebPart\WebPart.webpart'

SetupPath should be equal to the path given in the warning.

The resulting lines will show all elements still in SharePoint that reference the missing file.

Now, the elements can be deleted by using PowerShell:

$site = Get-SPSite -Identity <SiteID from SQL result>
$web = Get-SPWeb -Identity <WebID from SQL result> -Site $site
$file = $web.GetFile([guid]"<Id from SQL result>")

$file.Delete()

Make sure that the file being delete here is really no longer needed!

I have created a little script to help with finding the Ids and deleting the entries:

#Requires -PSSnapin Microsoft.SharePoint.PowerShell
<#
.Synopsis
 Deletes all references to files that have missing SetupFiles
 USE WITH CAUTION!
#>

[CmdletBinding(SupportsShouldProcess)]
param (
  [Parameter(Mandatory=$true)]
  [string]$ContenDatabase,
  [Parameter(Mandatory=$true)]
  [string]$MissingFileName,
  [switch]$Force
)

$ErrorActionPreference="Stop"

$contentDb = Get-SPContentDatabase $ContenDatabase

$data = new-object System.Data.DataSet

try {
  $connection = new-object System.Data.SqlClient.SqlConnection -ArgumentList @($contentDb.DatabaseConnectionString)
  $query = "select Id, SiteId, WebId from AllDocs with (NOLOCK) where SetupPath like '$MissingFileName'"
  $command = new-object System.Data.SqlClient.SqlCommand -ArgumentList @($query, $connection)
  $connection.Open()
  $adapter = new-object System.Data.SqlClient.SqlDataAdapter -ArgumentList @($command)
  $adapter.Fill($data) | Out-Null
} finally {
  $connection.dispose()
}

$data.Tables[0] | % {
	try {
		$web = Get-spweb $_.WebId -Site $_.SiteId
		$file = $web.GetFile($_.Id)
		if($PSCmdlet.ShouldProcess($file.ServerRelativeUrl)){
			if($Force.isPresent -or $PSCmdlet.ShouldContinue("Delete $($file.ServerRelativeUrl) ?", "Confirm")) {
				$file.Delete()
			}
		}
	} finally {
		$web.Dispose()
	}
}