microsoft/aspire · error · ArgumentException

Anonymous volumes cannot be read-only.

Error message

Anonymous volumes cannot be read-only.

What it means

An anonymous volume (a Volume mount with no source) is created by the runtime with an unpredictable name, so making it read-only is meaningless and almost certainly a mistake — there is no stable source to protect. When type is Volume, source is null/empty, and isReadOnly is true, the constructor throws ArgumentException on the isReadOnly parameter.

Solutions

  1. Set isReadOnly: false for anonymous volumes.
  2. Provide an explicit source path if the volume must be read-only.
  3. If it was meant to be a bind mount, switch the type back to BindMount and supply the absolute host path.

Example fix

// before
var mount = new ContainerMountAnnotation(null, "/cache", ContainerMountType.Volume, isReadOnly: true);
// after
var mount = new ContainerMountAnnotation(null, "/cache", ContainerMountType.Volume, isReadOnly: false);
Defensive patterns

Strategy: validation

Validate before calling

if (type == ContainerMountType.Volume && string.IsNullOrEmpty(source) && isReadOnly)
    throw new ArgumentException("Anonymous volumes cannot be read-only.", nameof(isReadOnly));

Try / catch

try
{
    var mount = new ContainerMountAnnotation(source, target, type, isReadOnly);
}
catch (ArgumentException ex) when (ex.ParamName == nameof(isReadOnly))
{
    logger.LogWarning("Anonymous volume at {Target} cannot be read-only; falling back to read-write", target);
    var mount = new ContainerMountAnnotation(source, target, type, isReadOnly: false);
}

Prevention

When it happens

Trigger: new ContainerMountAnnotation(null, "/data", ContainerMountType.Volume, isReadOnly: true) — declaring an anonymous volume and marking it read-only in the same call.

Common situations: Copy-pasting a bind-mount line, changing the type to Volume, but leaving isReadOnly: true; generating mounts from configuration where readonly is defaulted to true and the source was omitted; attempting to make a container-writable scratch location immutable.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/aa90139bcdda4740. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting/ApplicationModel/ContainerMountAnnotation.cs:39

    /// <param name="isReadOnly">A value indicating whether the mount is read-only.</param>
    public ContainerMountAnnotation(string? source, string target, ContainerMountType type, bool isReadOnly)
    {
        if (type == ContainerMountType.BindMount)
        {
            if (string.IsNullOrEmpty(source))
            {
                throw new ArgumentNullException(nameof(source), MessageStrings.ContainerMountBindMountsRequireSourceExceptionMessage);
            }

            if (!Path.IsPathRooted(source))
            {
                throw new ArgumentException(MessageStrings.ContainerMountBindMountsRequireRootedPaths, nameof(source));
            }
        }

        if (type == ContainerMountType.Volume && string.IsNullOrEmpty(source) && isReadOnly)
        {
            throw new ArgumentException(MessageStrings.ContainerMountAnonymousVolumesReadOnlyExceptionMessage, nameof(isReadOnly));
        }

        Source = source;
        Target = target;
        Type = type;
        IsReadOnly = isReadOnly;
    }

    /// <summary>
    /// Gets the source of the bind mount or name if a volume. Can be <c>null</c> if the mount is an anonymous volume.
    /// </summary>
    public string? Source { get; }

    /// <summary>
    /// Gets the target of the mount.
    /// </summary>
    public string Target { get; }

View on GitHub (pinned to 25830f84bd)