{"record":{"id":"a2f826767f08d77d","repo":"microsoft/aspire","slug":"bind-mounts-must-specify-a-source-path","errorCode":null,"errorMessage":"Bind mounts must specify a source path.","messagePattern":"Bind mounts must specify a source path\\.","errorType":"exception","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting/ApplicationModel/ContainerMountAnnotation.cs","lineNumber":28,"sourceCode":"/// Represents a mount annotation for a container resource.\n/// </summary>\n[DebuggerDisplay(\"Type = {GetType().Name,nq}, Source = {Source}, Target = {Target}\")]\npublic sealed class ContainerMountAnnotation : IResourceAnnotation\n{\n    /// <summary>\n    /// Instantiates a mount annotation that specifies the details for a container mount.\n    /// </summary>\n    /// <param name=\"source\">The source path if a bind mount or name if a volume. Can be <c>null</c> if the mount is an anonymous volume.</param>\n    /// <param name=\"target\">The target path of the mount.</param>\n    /// <param name=\"type\">The type of the mount.</param>\n    /// <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    }","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting/ApplicationModel/ContainerMountAnnotation.cs#L10-L46","documentation":"ContainerMountAnnotation represents a mount for a container. Bind mounts map a host path into the container, so a source path is mandatory; a volume mount can be anonymous but a bind mount cannot. When the type is BindMount and source is null or empty, the constructor throws ArgumentNullException naming the source parameter.","triggerScenarios":"Calling new ContainerMountAnnotation(null, target, ContainerMountType.BindMount, isReadOnly) or with \"\" as source — e.g. deriving the host path from a variable that is null at construction time.","commonSituations":"Mounting a host directory whose path is read from config or an environment variable that is unset; passing a source string built by string interpolation where a segment was empty; copying a volume-mount call and forgetting to switch the type or add a source.","solutions":["Pass a non-empty absolute host path as the source for bind mounts.","If the mount has no host source, use ContainerMountType.Volume instead of BindMount.","Guard the source value before constructing the annotation and throw a domain-specific error."],"exampleFix":"// before\nvar mount = new ContainerMountAnnotation(configHostPath, \"/config\", ContainerMountType.BindMount, false);\n// after\nif (string.IsNullOrEmpty(configHostPath)) throw new InvalidOperationException(\"Config host path must be set for bind mount.\");\nvar mount = new ContainerMountAnnotation(Path.GetFullPath(configHostPath), \"/config\", ContainerMountType.BindMount, false);","handlingStrategy":"validation","validationCode":"if (type == ContainerMountType.BindMount && string.IsNullOrEmpty(source))\n    throw new ArgumentException(\"Bind mounts require a source path.\", nameof(source));","typeGuard":null,"tryCatchPattern":"try\n{\n    var mount = new ContainerMountAnnotation(source, target, ContainerMountType.BindMount, isReadOnly);\n}\ncatch (ArgumentNullException ex) when (ex.ParamName == nameof(source))\n{\n    logger.LogError(ex, \"Bind mount to {Target} is missing a host source path\", target);\n    throw;\n}","preventionTips":["Never pass a possibly-null variable straight into ContainerMountAnnotation for bind mounts.","Use Path.GetFullPath on config-supplied paths before mounting.","Prefer named volumes (with source) when the host path may be absent.","Cover mount construction in unit tests with null/empty/relative sources."],"tags":["dotnet","aspire","containers","bind-mount"],"backgroundTag":"null-argument","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"}