{"record":{"id":"aa90139bcdda4740","repo":"microsoft/aspire","slug":"anonymous-volumes-cannot-be-read-only","errorCode":null,"errorMessage":"Anonymous volumes cannot be read-only.","messagePattern":"Anonymous volumes cannot be read-only\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting/ApplicationModel/ContainerMountAnnotation.cs","lineNumber":39,"sourceCode":"    /// <param name=\"isReadOnly\">A value indicating whether the mount is read-only.</param>\n    public ContainerMountAnnotation(string? source, string target, ContainerMountType type, bool isReadOnly)\n    {\n        if (type == ContainerMountType.BindMount)\n        {\n            if (string.IsNullOrEmpty(source))\n            {\n                throw new ArgumentNullException(nameof(source), MessageStrings.ContainerMountBindMountsRequireSourceExceptionMessage);\n            }\n\n            if (!Path.IsPathRooted(source))\n            {\n                throw new ArgumentException(MessageStrings.ContainerMountBindMountsRequireRootedPaths, nameof(source));\n            }\n        }\n\n        if (type == ContainerMountType.Volume && string.IsNullOrEmpty(source) && isReadOnly)\n        {\n            throw new ArgumentException(MessageStrings.ContainerMountAnonymousVolumesReadOnlyExceptionMessage, nameof(isReadOnly));\n        }\n\n        Source = source;\n        Target = target;\n        Type = type;\n        IsReadOnly = isReadOnly;\n    }\n\n    /// <summary>\n    /// Gets the source of the bind mount or name if a volume. Can be <c>null</c> if the mount is an anonymous volume.\n    /// </summary>\n    public string? Source { get; }\n\n    /// <summary>\n    /// Gets the target of the mount.\n    /// </summary>\n    public string Target { get; }\n","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting/ApplicationModel/ContainerMountAnnotation.cs#L21-L57","documentation":"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.","triggerScenarios":"new ContainerMountAnnotation(null, \"/data\", ContainerMountType.Volume, isReadOnly: true) — declaring an anonymous volume and marking it read-only in the same call.","commonSituations":"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.","solutions":["Set isReadOnly: false for anonymous volumes.","Provide an explicit source path if the volume must be read-only.","If it was meant to be a bind mount, switch the type back to BindMount and supply the absolute host path."],"exampleFix":"// before\nvar mount = new ContainerMountAnnotation(null, \"/cache\", ContainerMountType.Volume, isReadOnly: true);\n// after\nvar mount = new ContainerMountAnnotation(null, \"/cache\", ContainerMountType.Volume, isReadOnly: false);","handlingStrategy":"validation","validationCode":"if (type == ContainerMountType.Volume && string.IsNullOrEmpty(source) && isReadOnly)\n    throw new ArgumentException(\"Anonymous volumes cannot be read-only.\", nameof(isReadOnly));","typeGuard":null,"tryCatchPattern":"try\n{\n    var mount = new ContainerMountAnnotation(source, target, type, isReadOnly);\n}\ncatch (ArgumentException ex) when (ex.ParamName == nameof(isReadOnly))\n{\n    logger.LogWarning(\"Anonymous volume at {Target} cannot be read-only; falling back to read-write\", target);\n    var mount = new ContainerMountAnnotation(source, target, type, isReadOnly: false);\n}","preventionTips":["Only set isReadOnly: true when a concrete source path is supplied.","Default isReadOnly to false for volume mounts in helper methods.","Tie the readonly flag to the presence of a source in config-driven mount builders.","Add a unit test asserting anonymous+readonly is rejected."],"tags":["dotnet","aspire","containers","volumes"],"backgroundTag":"invalid-argument-value","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}