Post

Troubleshooting a Local NuGet Feed on macOS

Why a local NuGet feed on a Mac silently fails to serve your newest package, from macOS folder permissions and pinned versions to stale caches and flat feed layouts, and how to fix each one.

A local NuGet feed on a Mac is just a folder of .nupkg files listed in your nuget.config. Setting one up takes minutes. Working out why your newest package won’t appear can take an afternoon, because NuGet tends to fail quietly: it warns, falls back to the remote feeds, and restores the old version.

These are the problems I hit, roughly in the order you should check for them.

Start here: dotnet list package --outdated

Run this from the folder that contains your .sln:

dotnet list package --outdated

It queries every configured source and lists each package’s requested, resolved and latest versions. It also prints the sources it used and any warnings about them, so it splits the problem in two:

  • Your newer build shows under “Latest”: the feed works. The project just needs its version reference bumped (see below).
  • It doesn’t: NuGet can’t see the package in the feed. Read the warnings at the top of the output before anything else.

”Access to the path … is denied”

This was my actual problem, and it was buried in the output as a long run of identical lines:

warn : Access to the path '/Users/chris/Documents/Windows/NuGet' is denied.

NuGet couldn’t read the feed folder at all. Because an unreadable source is only a warning, restore carried on with nuget.org and my Azure DevOps feeds, and my own packages simply never showed a newer version.

The cause is macOS privacy protection. Apps can’t read ~/Documents, ~/Desktop or ~/Downloads until you grant them access. Terminal already had that permission; Rider didn’t. So the same feed worked from one and silently failed from the other.

There are two fixes, and both worked for me:

  1. Grant access. Open System Settings → Privacy & Security → Files & Folders and enable Documents Folder for your IDE. If it isn’t listed, add it under Full Disk Access. Restart the app.

  2. Move the feed out of Documents. This avoids the issue for every app, and keeps the feed away from iCloud sync, which can offload files to the cloud where NuGet can’t read them:

    mkdir -p ~/Development/NuGet
    mv ~/Documents/Windows/NuGet/* ~/Development/NuGet/

    Then point nuget.config at the new path: <add key="Local" value="/Users/chris/Development/NuGet" />.

”All projects are up-to-date for restore”

If nothing in the solution has changed since the last restore, NuGet skips the restore and never contacts a feed. Even -v detailed then tells you nothing about sources. Force a real restore, turn off the terminal logger so detailed output isn’t collapsed, and filter for your feed:

dotnet restore --force -v detailed --tl:off | grep -i Development/NuGet

Also, don’t read anything into your packages being absent from the restore output. It only lists warnings, so a package that resolved cleanly won’t appear.

A new build is in the feed but isn’t used

If --outdated shows the new version under “Latest” but the project still resolves the old one, one of two things is happening.

The reference is pinned. NuGet never upgrades a package on its own. <PackageReference Include="MyCompany.MyLibrary" Version="20261024.0.0" /> is a minimum, but NuGet resolves the lowest matching version, so in practice it never moves. Bump it, run dotnet add package MyCompany.MyLibrary to take the latest, or use a floating version (Version="*") if you always want the newest build.

You re-used a version number. NuGet caches packages by ID and version in ~/.nuget/packages. Push a changed package under an existing version and every project keeps the cached copy. Give every build a new version; date-based versions such as yyyyMMdd.rev from your CI pipeline make this automatic. If you’re stuck with a re-used version, clear the cache:

dotnet nuget locals all --clear

The IDE shows a stale package list

If the command line sees the new version and your IDE doesn’t, first rule out the permissions problem above, since the IDE may lack access that Terminal has. After that, it’s usually a cached view:

  • Rider: in the NuGet tool window (View → Tool Windows → NuGet), click the refresh button. If that doesn’t help, use File → Invalidate Caches… and restart.
  • VS Code: run Developer: Reload Window from the Command Palette (⌘⇧P).

Packages land flat instead of in id/version/ folders

I expected dotnet nuget push to file packages as mycompany.mylibrary/1.0.0/mycompany.mylibrary.1.0.0.nupkg. Instead they all landed at the root of the folder.

That’s by design. When pushing to a folder, push follows the layout already there. An empty folder, or one with loose .nupkg files at the root, is treated as a flat feed.

A flat feed restores perfectly well; the hierarchical layout only speeds things up once a feed holds hundreds of packages. If you’d still like it, seed the folder with one correctly laid-out package and push follows suit from then on. I cover how in Creating a Hierarchical Local NuGet Feed on macOS with dotnet.

Eventually, I found the solution was to seed the feed with the correct structure, and all subsequent dotnet push command followed what was already there. The seeding process can be found in hierarchical-local-nuget-feed-macos.

Two related gotchas:

  • The file name always includes the version. push names each file from the package’s .nuspec, so a build setting like OutputFileNamesWithoutVersion=true is undone when you push. That’s a good thing: it lets several versions sit side by side.
  • It’s --source, not source. dotnet nuget push "*.nupkg" --source ~/Development/NuGet (or -s). Without the dashes, the path isn’t treated as the source.

Other things to rule out

Files owned by root. If anything touched the feed with sudo, NuGet may not be able to read it. Check with ls -la ~/Development/NuGet and fix ownership with:

sudo chown -R $(whoami):staff ~/Development/NuGet

Package source mapping. If your nuget.config has a <packageSourceMapping> section, NuGet only looks for each package in the sources mapped to it. Add a pattern for your own packages under the local source:

<packageSource key="Local">
  <package pattern="MyCompany.*" />
</packageSource>

A feed you’ve moved. Update every reference to the old path: the solution’s nuget.config, your push command, and any user-level source added with dotnet nuget add source. List them with dotnet nuget list source and remove stale ones with dotnet nuget remove source <name>. Otherwise the access warnings come back from a folder that no longer matters.

Wrapping up

Most of the time, NuGet has already told you what’s wrong; it just said it quietly. Run dotnet list package --outdated, read the warnings at the top before anything else, and on a Mac suspect folder permissions first.

← Back to all posts