Package Metadata, README, Icon and License

This section documents MSBuild 18.9, the .NET SDK 10.0.402 (with its dotnet CLI) and NuGet 7.9, as published in the MSBuild, .NET and NuGet documentation on learn.microsoft.com, which is the reference these pages are written and verified against. Material about .NET 11 / NuGet 8.0 is still in preview and is always flagged as such — never presented as baseline.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The C# Reference bibliography lists the reference material consulted while preparing these pages.

Metadata is what a consumer sees before deciding to install a package: the ID and description in search results, the README, icon and licence on the package page, the repository link that lets them check the source. NuGet’s package authoring best practices give a rule for each property; this page lists them, packs Ledgerline with all of them, and reproduces every pack error for the README, icon and licence with .NET SDK 10.0.402 (NuGet 7.9.0).

The properties

MSBuild property .nuspec element Rule

PackageId

id

✔️ DO choose a unique ID, clearly different from existing ones; ✔️ CONSIDER a prefix that meets the prefix-reservation criteria. Defaults to the assembly name.

Version / PackageVersion

version

✔️ CONSIDER SemVer; ✔️ DO publish a prerelease if the package is not stable (Versioning).

Authors

authors

✔️ DO use the "pretty name" of the person or organization. Defaults to the assembly name.

Description

description

✔️ DO include a short description (up to 4000 characters). Defaults to Package Description.

Copyright

copyright

✔️ DO add Copyright (c) <name> <year>.

PackageProjectUrl

projectUrl

✔️ DO link the project, repository or website.

RepositoryUrl, RepositoryType, RepositoryBranch, RepositoryCommit

repository

Filled in by Source Link; set PublishRepositoryUrl to publish the URL (Symbols and Source Link).

PackageTags

tags

✔️ DO include several tags (semicolon-separated in MSBuild, space-separated in the .nuspec).

PackageReleaseNotes

releaseNotes

✔️ DO include release notes with each update.

PackageRequireLicenseAcceptance

requireLicenseAcceptance

false by default; true makes clients prompt before installing.

Title

title

Human-friendly title; defaults to the ID.

PackageReadmeFile, PackageIcon, PackageLicenseExpression, PackageLicenseFile

readme, icon, license

See the sections below.

Owners and Summary from the .nuspec have no MSBuild equivalent. An ID must be at most 100 characters, use only ASCII letters, digits, dots and dashes, and must not contain consecutive dots or dashes. The two documents disagree on the first character: nuget.org’s publishing page allows a letter, digit or underscore, the NU5052 reference a letter or digit. With SDK 10.0.402, invalid IDs failed with errors that carry no code — not the NU5052 warning the reference describes:

error : The package ID 'Ledger line!' contains invalid characters. Examples of valid package IDs include 'MyPackage' and 'MyPackage.Sample'.
error : Id must not exceed 100 characters.

A project with no metadata at all packs without a warning, as <authors>Defaults</authors> and <description>Package Description</description> — nuget.org accepts that, so nothing but review stops it.

The Ledgerline metadata

Shared values live in Directory.Build.props; the README and icon at the repository root are packed by Directory.Build.targets for every packable project:

<!-- Directory.Build.props -->
<PropertyGroup>
  <Authors>Ledgerline contributors</Authors>
  <Copyright>Copyright (c) Ledgerline contributors</Copyright>
  <RepositoryUrl>https://example.com/ledgerline/ledgerline</RepositoryUrl>
  <RepositoryType>git</RepositoryType>
  <PackageProjectUrl>https://example.com/ledgerline</PackageProjectUrl>
  <PackageLicenseExpression>MIT</PackageLicenseExpression>
  <PackageReadmeFile>README.md</PackageReadmeFile>
  <PackageIcon>icon.png</PackageIcon>
</PropertyGroup>

<!-- Directory.Build.targets -->
<ItemGroup Condition="'$(IsPackable)' == 'true'">
  <None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" PackagePath="\" Visible="false" />
  <None Include="$(MSBuildThisFileDirectory)icon.png" Pack="true" PackagePath="\" Visible="false" />
</ItemGroup>

The project adds the ID, version, description and tags. The .nuspec inside the packed Ledgerline.1.0.0.nupkg:

<metadata>
  <id>Ledgerline</id>
  <version>1.0.0</version>
  <authors>Ledgerline contributors</authors>
  <license type="expression">MIT</license>
  <licenseUrl>https://licenses.nuget.org/MIT</licenseUrl>
  <icon>icon.png</icon>
  <readme>README.md</readme>
  <projectUrl>https://example.com/ledgerline</projectUrl>
  <description>Double-entry bookkeeping ledgers: read journal CSV files, check that every entry balances and export a trial balance as JSON.</description>
  <copyright>Copyright (c) Ledgerline contributors</copyright>
  <tags>ledger accounting double-entry trial-balance csv</tags>
  <repository type="git" url="https://example.com/ledgerline/ledgerline" commit="e33226ff8326b40770b01db9cc78bfd2d7044fc8" />
  ...
</metadata>

Pack added two things nobody wrote: a licenseUrl pointing to licenses.nuget.org, for clients older than the license element, and the git commit of the build, from Source Link.

The README

PackageReadmeFile names a Markdown file in the package (added in NuGet 5.10 preview 2, per the .nuspec reference; the release notes don’t mention it). The file must be packed explicitly, as above, and nuget.org renders it on the package page. The rules:

  • ✔️ DO add a README that says what the package does and how to get started — a short code sample, links to docs, the licence and how to get help.

  • nuget.org supports CommonMark through Markdig: headers, images, emphasis, lists, links, block quotes, code spans with syntax highlighting, task lists, tables, emoji and auto-links — no raw HTML.

  • Images and badges render only from an allow-list of domains — among them img.shields.io, badgen.net, codecov.io, coveralls.io, dev.azure.com, raw.githubusercontent.com, user-images.githubusercontent.com and GitHub Actions workflow badges. Relative image paths and other domains are not rendered, with a warning visible only to the owners.

  • To preview it, start an upload on nuget.org’s Upload Package page and look at the "Readme File" section before submitting; fixing it later needs a new version.

The pack errors, each reproduced with a one-file project:

error NU5039: The readme file 'README.md' does not exist in the package.
error NU5038: The readme file 'readme.txt' has an invalid extension. It must end in .md.
error NU5040: The readme file 'README.md' is empty.

NU5039 is what most people meet first: setting PackageReadmeFile without the None ... Pack="true" item.

The icon

PackageIcon names a PNG or JPEG inside the package, at most 1 MB; 128×128 is recommended. It replaced PackageIconUrl in NuGet 5.3 / Visual Studio 16.3. The icon.png of Ledgerline is a 128×128 RGBA PNG of 680 bytes.

error NU5046: The icon file 'icon.png' does not exist in the package.
error NU5045: The 'icon' element 'icon.bmp' has an invalid file extension. Valid options are .png, .jpg or .jpeg.
error NU5047: The icon file size must not exceed 1 megabyte.
warning NU5048: The 'PackageIconUrl'/'iconUrl' element is deprecated. Consider using the 'PackageIcon'/'icon' element instead. Learn more at https://aka.ms/deprecateIconUrl

The documentation contradicts itself on icons. The best-practices page says ✔️ DO use a 128×128 image with a transparent background and ❌ DO NOT use the deprecated IconUrl; the library guidance’s NuGet page (2026) still says ✔️ DO use an HTTPS href to the icon at 64×64; and the pack-target reference suggests setting PackageIconUrl in addition to PackageIcon "for the best downlevel experience", which triggers no warning when both are present. Embed a 128×128 PNG; add PackageIconUrl only if very old clients matter.

The licence

A package declares its licence in one of two ways (since NuGet 4.9):

  • PackageLicenseExpression — an SPDX expression such as MIT, Apache-2.0 or MIT OR Apache-2.0. Pack adds licenseUrl pointing to https://licenses.nuget.org/<expression>;, which shows the SPDX text. nuget.org accepts only identifiers approved by the OSI or the FSF.

  • PackageLicenseFile — a .txt or .md file packed into the package, for custom or non-standard licences.

The best practices: ✔️ DO include a licence expression or file; ❌ DO NOT use the deprecated PackageLicenseUrl; ✔️ CONSIDER an expression for open-source packages (consumers and tools read it directly); ✔️ DO include a licence file for closed-source ones. The errors:

error NU5030: The license file 'LICENSE.txt' does not exist in the package.
error NU5032: The license expression 'GPL-2.0' cannot be parsed succesfully. The identifier 'GPL-2.0' is deprecated.
error NU5033: Invalid metadata. Cannot specify both a PackageLicenseExpression and a PackageLicenseFile.
error NU5035: The PackageLicenseUrl is being deprecated and cannot be used in conjunction with the PackageLicenseFile or PackageLicenseExpression.
warning NU5124: The license identifier 'Foo-1.0' is not recognized by the current toolset.
warning NU5125: The 'licenseUrl' element will be deprecated. Consider using the 'license' element instead.

(NU5032 is quoted as printed, typo included; GPL-2.0 is deprecated in SPDX in favour of GPL-2.0-only and GPL-2.0-or-later.) NU5124 is a warning because the client’s SPDX list can be older than the licence — but nuget.org validates the expression on upload against its own list. A project with no licence at all packs without any warning.

Common mistakes

  • Setting PackageReadmeFile or PackageIcon without packing the file — NU5039 and NU5046.

  • Badges from an unlisted domain — they silently disappear on nuget.org.

  • Leaving Authors and Description at their defaults — the package says Package Description forever (metadata cannot be edited after upload).

  • A deprecated SPDX identifier — use GPL-2.0-only, not GPL-2.0.

  • Putting both a licence expression and a licence file — NU5033.