.
Symptom
Trying to install a NuGet V2 package from a proxy repository to https://www.powershellgallery.com/api/v2 with Install-Package command fails with message End of Central Directory record could not be found.
Example:
Install-Module -Name PSWSMan -Repository nexus -Force -Verbose
...
VERBOSE: Completed downloading 'PSWSMan'.
VERBOSE: Hash for package 'PSWSMan' does not match hash provided from the server.
VERBOSE: InstallPackageLocal' - name='PSWSMan', version='2.3.1',destination='/var/folders/6r/sgs9v4b17gl3kwhclfgdmndcv35z57/T/857063567'
Install-Package: Package 'PSWSMan' failed to be installed because: End of Central Directory record could not be found.
Explanation
The message "End of Central Directory record could not be found.", usually means that the remotes that Nexus Repo is trying to reach are being blocked by a firewall of some sort, or there is a network communication failure. The remotes that Sonatype Nexus Repo will need to reach will not just be domain powershellgallery.com , due to how servers at that site will do 302 redirects to packages hosted on other domains.
For example you can see below that the working direct build against powershell gallery ultimately redirects to https://www.powershellgallery.com/api/v2/package/PSWSMan/2.3.1
> Install-Module -Name PSWSMan -Repository PSGallery -Force -Verbose
VERBOSE: Suppressed Verbose Repository details, Name = 'PSGallery', Location = 'https://www.powershellgallery.com/api/v2'; IsTrusted = 'False'; IsRegistered = 'True'.
VERBOSE: Repository details, Name = 'PSGallery', Location = 'https://www.powershellgallery.com/api/v2'; IsTrusted = 'False'; IsRegistered = 'True'.
VERBOSE: Using the provider 'PowerShellGet' for searching packages.
VERBOSE: Using the specified source names : 'PSGallery'.
VERBOSE: Getting the provider object for the PackageManagement Provider 'NuGet'.
VERBOSE: The specified Location is 'https://www.powershellgallery.com/api/v2' and PackageManagementProvider is 'NuGet'.
VERBOSE: Searching repository 'https://www.powershellgallery.com/api/v2/FindPackagesById()?id='PSWSMan'' for ''.
VERBOSE: Total package yield:'1' for the specified package 'PSWSMan'.
VERBOSE: Performing the operation "Install-Module" on target "Version '2.3.1' of module 'PSWSMan'".
VERBOSE: The installation scope is specified to be 'CurrentUser'.
VERBOSE: The specified module will be installed in '/Users/ab515zz/.local/share/powershell/Modules'.
VERBOSE: The specified Location is 'NuGet' and PackageManagementProvider is 'NuGet'.
VERBOSE: Downloading module 'PSWSMan' with version '2.3.1' from the repository 'https://www.powershellgallery.com/api/v2'.
VERBOSE: Searching repository 'https://www.powershellgallery.com/api/v2/FindPackagesById()?id='PSWSMan'' for ''.
VERBOSE: InstallPackage' - name='PSWSMan', version='2.3.1',destination='/var/folders/6r/sgs9v4b17gl3kwhclfgdmndcv35z57/T/501863862'
VERBOSE: DownloadPackage' - name='PSWSMan', version='2.3.1',destination='/var/folders/6r/sgs9v4b17gl3kwhclfgdmndcv35z57/T/501863862/PSWSMan.2.3.1/PSWSMan.2.3.1.nupkg', uri='https://www.powershellgallery.com/api/v2/package/PSWSMan/2.3.1'
VERBOSE: Downloading 'https://www.powershellgallery.com/api/v2/package/PSWSMan/2.3.1'.
VERBOSE: Completed downloading 'https://www.powershellgallery.com/api/v2/package/PSWSMan/2.3.1'.
VERBOSE: Completed downloading 'PSWSMan'.
VERBOSE: InstallPackageLocal' - name='PSWSMan', version='2.3.1',destination='/var/folders/6r/sgs9v4b17gl3kwhclfgdmndcv35z57/T/501863862'
VERBOSE: Validating the 'PSWSMan' module contents under '/var/folders/6r/sgs9v4b17gl3kwhclfgdmndcv35z57/T/501863862/PSWSMan.2.3.1' path.
VERBOSE: Test-ModuleManifest successfully validated the module manifest file '/var/folders/6r/sgs9v4b17gl3kwhclfgdmndcv35z57/T/501863862/PSWSMan.2.3.1'.
VERBOSE: Module 'PSWSMan' was installed successfully to path '/Users/ab515zz/.local/share/powershell/Modules/PSWSMan/2.3.1'.
If you take this URL and test it will curl, you will see the remote will 302 redirect that URL to https://psg-prod-eastus.azureedge.net/packages/pswsman.2.3.1.nupkg. This is exactly what Nexus Repo will attempt to do.
> curl -v https://www.powershellgallery.com/api/v2/package/PSWSMan/2.3.1 -o /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 40.122.208.145:443...
* Connected to www.powershellgallery.com (40.122.208.145) port 443 (#0)
* ALPN: offers h2,http/1.1
} [5 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [94 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [3826 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [365 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [102 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN: server accepted h2
* Server certificate:
* subject: C=US; ST=WA; L=Redmond; O=Microsoft Corporation; CN=www.powershellgallery.com
* start date: Mar 14 00:14:27 2023 GMT
* expire date: Sep 10 00:14:27 2023 GMT
* issuer: C=US; O=Microsoft Corporation; CN=Microsoft Azure TLS Issuing CA 05
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
{ [5 bytes data]
* using HTTP/2
* h2 [:method: GET]
* h2 [:scheme: https]
* h2 [:authority: www.powershellgallery.com]
* h2 [:path: /api/v2/package/PSWSMan/2.3.1]
* h2 [user-agent: curl/8.1.2]
* h2 [accept: */*]
* Using Stream ID: 1 (easy handle 0x7ff78c816200)
} [5 bytes data]
> GET /api/v2/package/PSWSMan/2.3.1 HTTP/2
> Host: www.powershellgallery.com
> User-Agent: curl/8.1.2
> Accept: */*
>
{ [5 bytes data]
< HTTP/2 302
< cache-control: private
< content-type: text/html; charset=utf-8
< location: https://psg-prod-eastus.azureedge.net/packages/pswsman.2.3.1.nupkg
< server: Microsoft-IIS/10.0
< content-security-policy: frame-ancestors 'none'
< x-frame-options: deny
< x-xss-protection: 1; mode=block
< x-content-type-options: nosniff
< strict-transport-security: max-age=31536000
< date: Mon, 19 Jun 2023 20:19:42 GMT
< content-length: 183
<
{ [183 bytes data]
100 183 100 183 0 0 370 0 --:--:-- --:--:-- --:--:-- 373
* Connection #0 to host www.powershellgallery.com left intact
In the above example you can see to successfully proxy the remote file, access to URL https://psg-prod-eastus.azureedge.net/packages/pswsman.2.3.1.nupkg will be needed by Nexus Repo as per the 302 redirect location header.
Advice
The remotes domains a remote site may redirect are subject to change. Sonatype Nexus Repo will try to follow standard HTTP redirects to complete inbound requests.
Please ensure that Nexus Repo has the required network access to domains needed.
If you need more evidence on Nexus Repo side this is the cause of this probem, these steps may help to further debug the problem
- Clean local cache of nuget client location: nuget locals -clear all
- Clear cache of the proxy repository in Nexus Repo by going to the proxy repo settings and clicking Invalidate Cache
- Go to Nexus Repo Logging admin and change logger org.sonatype.nexus.httpclient.outbound to DEBUG level per this article: How to Debug Outbound HTTP Requests in Repository 3
- After recreating the error End of Central Directory record could not be found with the client, reset the logger level of org.sonatype.nexus.httpclient.outbound back to INFO level
- Examine the network traffic logged in nexus.log for your inbound request looking for 302 redirects to other domains. Verify the response was a valid 200 response and that there were no errors.